Multi-line Memoization

Here’s a quick tip that came out of a code review we did last week. One easy way to add caching to your Ruby app is to memoize the results of computationally expensive methods:

def foo @foo ||= expensive_method end 

The first time the method is called, @foo will be nil, so expensive_method will be called and its result stored in @foo. On subsequent calls, @foo will have a value, so the call to expensive_method will be bypassed. This works well for one-liners, but what if our method requires multiple lines to determine its result?

def foo arg1 = expensive_method_1 arg2 = expensive_method_2 expensive_method_3(arg1, arg2) end 

A first attempt at memoization yields this:

def foo unless @foo arg1 = expensive_method_1 arg2 = expensive_method_2 @foo = expensive_method_3(arg1, arg2) end @foo end 

To me, using @foo three times obscures the intent of the method. Let’s do this instead:

def foo @foo ||= begin arg1 = expensive_method_1 arg2 = expensive_method_2 expensive_method_3(arg1, arg2) end end 

This clarifies the role of @foo and reduces LOC. Of course, if you use the Rails built-in memoize method, you can avoid accessing these instance variables entirely, but this technique has utility in situations where requiring ActiveSupport would be overkill.

David is Viget's managing development director. From our Durham, NC, office, he builds high-quality, forward-thinking software for PUMA, the World Wildlife Fund, ID.me, and many others.

More posts by David