{"success":true,"data":{"title":"Begin; Return; Shenanigans","slug":"begin-return-shenanigans","excerpt":"When using Ruby's begin block to memoize the result of an operation beware mixing it with return statements. Here's why.","has_code_sample":true,"published_at":"2022-12-19T16:15:00.000Z","content":"\u003cp\u003eMemoization is a neat way to improve performance — it allows you to cache the result of expensive operations to re-use the result without paying the cost. We can combine memoization with the \u003ccode\u003ebegin\u003c/code\u003e block when using Ruby. By combining these two language features, we can cache the results of more complex logic.\u003c/p\u003e\n\n\u003cp\u003eTake the following example:\u003c/p\u003e\n\n\u003cdiv class=\"language-ruby highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eCost\u003c/span\u003e\n  \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003einitialize\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eexpensive\u003c/span\u003e\u003cspan class=\"p\"\u003e:)\u003c/span\u003e\n    \u003cspan class=\"vi\"\u003e@expensive\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eexpensive\u003c/span\u003e\n  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\n  \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003edescription\u003c/span\u003e\n    \u003cspan class=\"vi\"\u003e@description\u003c/span\u003e \u003cspan class=\"o\"\u003e||=\u003c/span\u003e \u003cspan class=\"k\"\u003ebegin\u003c/span\u003e\n      \u003cspan class=\"c1\"\u003e# An expensive operation.\u003c/span\u003e\n      \u003cspan class=\"nb\"\u003esleep\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\n\n      \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"ss\"\u003e:expensive\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"vi\"\u003e@expensive\u003c/span\u003e\n      \u003cspan class=\"ss\"\u003e:cheap\u003c/span\u003e\n    \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003cp\u003eHere we have a class, \u003ccode\u003eCost\u003c/code\u003e, which has a method, \u003ccode\u003edescription\u003c/code\u003e, that returns a description of the cost (specifically “expensive” or “cheap”). For this example, the class takes a single argument when initialized — \u003ccode\u003eexpensive\u003c/code\u003e. The \u003ccode\u003edescription\u003c/code\u003e method will sleep for one second and then return “expensive” when we initialize the class with \u003ccode\u003eexpensive: true\u003c/code\u003e, or “cheap” when we initialize it with any other value. The critical thing to note here is that, regardless of the value of \u003ccode\u003eexpensive\u003c/code\u003e, the method sleeps for one second and then provides its value.\u003c/p\u003e\n\n\u003cp\u003eLet’s test how this code performs — we’ll use Ruby’s built-in Benchmark module to measure how long the execution takes for five calls of the \u003ccode\u003edescription\u003c/code\u003e method when the class is initialized as expensive and inexpensive.\u003c/p\u003e\n\n\u003cdiv class=\"language-ruby highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"n\"\u003echeap\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003eCost\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"ss\"\u003eexpensive: \u003c/span\u003e\u003cspan class=\"kp\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003cspan class=\"n\"\u003eexpensive\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003eCost\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"ss\"\u003eexpensive: \u003c/span\u003e\u003cspan class=\"kp\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\n\u003cspan class=\"no\"\u003eBenchmark\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003erealtime\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n  \u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003etimes\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003echeap\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003edescription\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\n\u003cspan class=\"c1\"\u003e# =\u0026gt; 1.0011720000766218\u003c/span\u003e\n\n\u003cspan class=\"no\"\u003eBenchmark\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003erealtime\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n  \u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003etimes\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"n\"\u003eexpensive\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003edescription\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\n\u003cspan class=\"c1\"\u003e# =\u0026gt; 5.003925999975763\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003cp\u003eIn the first example, it takes about 1 second to call the method five times — this makes sense — the first time we run the method, the code sleeps for one second and returns its value which gets memoized. However, in the second example, the five executions take five seconds which suggests that the result isn’t being memoized…and it isn’t, but why?\u003c/p\u003e\n\n\u003cp\u003eLet’s look at another example.\u003c/p\u003e\n\n\u003cdiv class=\"language-ruby highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003esay_hello\u003c/span\u003e\n  \u003cspan class=\"k\"\u003ebegin\u003c/span\u003e\n    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\n  \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\n  \u003cspan class=\"nb\"\u003eputs\u003c/span\u003e \u003cspan class=\"s1\"\u003e'Hello World'\u003c/span\u003e\n\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\n\u003cspan class=\"c1\"\u003e# =\u0026gt; nil\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003cp\u003eWhen we call the \u003ccode\u003esay_hello\u003c/code\u003e method, the function returns \u003ccode\u003enil\u003c/code\u003e and “Hello World” is never put. In this example, code after returning from the \u003ccode\u003ebegin\u003c/code\u003e block never executes. In the first example, memoization never happens following a \u003ccode\u003ereturn\u003c/code\u003e. Both of these things give a clue about what’s going on.\u003c/p\u003e\n\n\u003cp\u003eWhen we use a \u003ccode\u003ebegin\u003c/code\u003e block, it’s easy to assume that it would behave like any other block — you’d think that returning from the block gives execution back to the method that called it. What happens is returning from the \u003ccode\u003ebegin\u003c/code\u003e block returns us from the function as well. This explains why the value never got memoized in the first example and the world was never greeted in the second. When we returned from the \u003ccode\u003ebegin\u003c/code\u003e block, we were not executing anything after it as we’d left the function. \u003ca href=\"https://docs.ruby-lang.org/en/2.4.0/syntax/exceptions_rdoc.html\"\u003e\u003ccode\u003ebegin\u003c/code\u003e blocks are for handling exceptions\u003c/a\u003e. Here are two equivalent examples, one where we handle exceptions in a method and one where we aren’t.\u003c/p\u003e\n\n\u003cdiv class=\"language-ruby highlighter-rouge\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre class=\"highlight\"\u003e\u003ccode\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eexceptional\u003c/span\u003e\n  \u003cspan class=\"k\"\u003eraise\u003c/span\u003e \u003cspan class=\"no\"\u003eStandardError\u003c/span\u003e\n\u003cspan class=\"k\"\u003erescue\u003c/span\u003e \u003cspan class=\"no\"\u003eStandardError\u003c/span\u003e\n  \u003cspan class=\"c1\"\u003e# Handle the exception.\u003c/span\u003e\n\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\n\u003cspan class=\"k\"\u003ebegin\u003c/span\u003e\n  \u003cspan class=\"k\"\u003eraise\u003c/span\u003e \u003cspan class=\"no\"\u003eStandardError\u003c/span\u003e\n\u003cspan class=\"k\"\u003erescue\u003c/span\u003e\n  \u003cspan class=\"c1\"\u003e# Handle the exception.\u003c/span\u003e\n\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\n\u003cp\u003eAlthough the \u003ccode\u003ebegin\u003c/code\u003e keyword isn’t used in the method (because we want to rescue from an error anywhere in the function rather than scope it to a specific block of code), the two examples are essentially the same. Not having the \u003ccode\u003ebegin\u003c/code\u003e keyword helps demonstrate what is going on when we call \u003ccode\u003ereturn\u003c/code\u003e from a \u003ccode\u003ebegin\u003c/code\u003e block — we return from the method, not the block. So, if you use this memoization style to help improve your app’s performance, avoid calls to \u003ccode\u003ereturn\u003c/code\u003e — it may not behave as you expect.\u003c/p\u003e\n","tags":["Ruby"]}}