Dry::Struct auto-memoization?


#1

Consider the following, which prints ‘calling full’ twice:

module Types
  include Dry::Types.module
  NonEmptyString = Types::Strict::String.constrained(format: /\S+/)
end

class Person < Dry::Struct
  attribute :first, Types::NonEmptyString
  attribute :last, Types::NonEmptyString
  def full
    puts 'calling full'
    first + ' ' + last
  end
end

p = Person.new(first: 'John', last: 'Smith')
p p.full
p p.full

Of course I manually memoize full, or do it with a gem like memoist, etc:

  def full
    return @full if @full
    puts 'calling full'
    @full = first + ' ' + last
  end

But with an immutable value object, this is redundant in a sense, because any function of my immutable values is guaranteed by referential transparency to be safe to memoize.

While there may be some edge cases where you don’t want to memoize for reasons like memory consumption, you very often do.

Does Dry::Struct offer any mechanism by which I can, at one fell swoop, say “memoize all the methods I add this value object”?

Or is Dry::Struct intended only for the base data, preferring to put “derived” data like the method full above into decorator objects?

I would love to hear any thoughts on this.


#2

@jonahx Personally, I have no problem with a method like this…

  def full
    first + ' ' + last
  end

…being fully executed upon each invocation. It’s simply not a slow method. Memoization feels like overkill here.

I feel like having dry-struct automatically memoize ordinary methods added to the class would violate principle of least surprise. This is not how usual Ruby classes work, and I don’t think people would consider dry-struct’s purview to cover this, particularly when there are any number of memoizing libs already available (including Dry::Core::Memoizable).

If you wanted all your structs to memoize all their methods, one way would be to create your own in-app struct superclass which inherits from Dry::Struct and also implements its own .method_added which can then add in the memoizing logic.


#3

Thanks @timriley.

Yeah that one ofc doesn’t matter… it was merely illustrative of the concept. You can imagine cases where the computation is slow enough that you want memoization, and I think it doesn’t hurt for cases like this.

and I don’t think people would consider dry-struct’s purview to cover this

I was imagining it would be opt in.

any number of memoizing libs already available (including Dry::Core::Memoizable ).

Had been looking at memoist but wasn’t aware Dry provided one, thanks for pointing this out.

would be to create your own in-app struct superclass which inherits from Dry::Struct and also implements its own .method_added which can then add in the memoizing logic.

I like this, thanks.