rubymemoization

Break out of a begin/end block early


I want a way to exit a begin/end block while still assigning the variable that its result is assigned to.

def foo
  @foo ||= begin
    puts "running"

    return "leaving early" if true # would be some sort of calculation

    # Other calculations
  end
end

What I hope to happen

> foo
running
=> leaving early
> foo
=> leaving early

What actually happens

> foo
running
=> leaving early
> foo
running
=> leaving early

The code doesn't work because return exits the entire method without setting @foo. Using break or next only work in loops. Does anything work within a begin block the way I'm thinking?

Current ways I can do it but was hoping to avoid:

There seem to be a lot of related questions about breaking out of blocks but I couldn't find one that answers this specific version (maybe because it's not possible).


Solution

  • I think you're going to save yourself a whole heck of a lot of strife if you just put all that logic into its own method:

    def foo
      @foo ||= compute_foo
    end
    
    def compute_foo
      puts "running"
    
      return "leaving early" if true # would be some sort of calculation
    
      # Other calculations
    end
    

    This decouples the computation from the memoization, making it easier to test and reason about, and it's a fairly common design pattern in Ruby and other languages.

    Of course, there are ways of doing what you're asking. The most obvious solution being an immediately-invoked anonymous proc:

    def foo
      @foo ||= (proc do
        puts "running"
    
        next "leaving early" if true # would be some sort of calculation
    
        # Other calculations
      end)[] # or .call or .()
    end
    

    But you certainly wouldn't be doing yourself or any future maintainers of this code any favors.