13.4: Returning from Inside a Block
- Page ID
- 43739
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\( \newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\)
( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\id}{\mathrm{id}}\)
\( \newcommand{\Span}{\mathrm{span}}\)
\( \newcommand{\kernel}{\mathrm{null}\,}\)
\( \newcommand{\range}{\mathrm{range}\,}\)
\( \newcommand{\RealPart}{\mathrm{Re}}\)
\( \newcommand{\ImaginaryPart}{\mathrm{Im}}\)
\( \newcommand{\Argument}{\mathrm{Arg}}\)
\( \newcommand{\norm}[1]{\| #1 \|}\)
\( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\)
\( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\AA}{\unicode[.8,0]{x212B}}\)
\( \newcommand{\vectorA}[1]{\vec{#1}} % arrow\)
\( \newcommand{\vectorAt}[1]{\vec{\text{#1}}} % arrow\)
\( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vectorC}[1]{\textbf{#1}} \)
\( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)
\( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)
\( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)In this section we explain why it is not a good idea to have return statements inside a block (such as [^ 33]
) that you pass or store into instance variables. A block with an explicit return statement is called a non-local returning block. Let us start illustrating some basic points first.
Basics on return
By default the returned value of a method is the receiver of the message i.e., self. A return expression (the expression starting with the character ^
) allows one to return a different value than the receiver of the message. In addition, the execution of a return statement exits the currently executed method and returns to its caller. This ignores the expressions following the return statement.
Experiment 7: Return's Exiting Behavior. Define the following method. Executing Bexp new testExplicitReturn
prints ’one’ and ’two’ but it will not print not printed
, since the method testExplicitReturn
will have returned before.
Bexp>>testExplicitReturn self traceCr: 'one'. 0 isZero ifTrue: [ self traceCr: 'two'. ^ self]. self traceCr: 'not printed'
Note that the return expression should be the last statement of a block body.
Escaping behavior of non-local return
A return expression behaves also like an escaping mechanism since the execution flow will directly jump out to the current invoking method. Let us define a new method jumpingOut
as follows to illustrate this behavior.
Bexp>>jumpingOut #(1 2 3 4) do: [:each | self traceCr: each printString. each = 3 ifTrue: [^ 3]]. ^ 42 Bexp new jumpingOut → 3
For example, the following expression Bexp new jumpingOut
will return 3 and not 42. ^ 42
will never be reached. The expression [ ^3 ]
could be deeply nested, its execution jumps out all the levels and return to the method caller. Some old code (predating introduction of exceptions) passes non-local returning blocks around leading to complex flows and difficult to maintain code. We strongly suggest not using this style because it leads to complex code and bugs. In subsequent sections we will carefully look at where a return is actually returning.
Understanding return
Now to see that a return is really escaping the current execution, let us build a slightly more complex call flow. We define four methods among which one (defineBlock
) creates an escaping block, one (arg:
) evaluates this block and one (evaluatingBlock:
) that executes the block. Note that to stress the escaping behavior of a return we defined evaluatingBlock:
so that it endlessly loops after evaluating its argument.
Bexp>>start | res | self traceCr: 'start start'. res := self defineBlock. self traceCr: 'start end'. ^ res Bexp>>defineBlock | res | self traceCr: 'defineBlock start'. res := self arg: [ self traceCr: 'block start'. 1 isZero ifFalse: [ ^ 33 ]. self traceCr: 'block end'. ]. self traceCr: 'defineBlock end'. ^ res Bexp>>arg: aBlock | res | self traceCr: 'arg start'. res := self evaluateBlock: aBlock. self traceCr: 'arg end'. ^ res Bexp>>evaluateBlock: aBlock | res | self traceCr: 'evaluateBlock start'. res := self evaluateBlock: aBlock value. self traceCr: 'evaluateBlock loops so should never print that one'. ^ res
Executing Bexp new
start prints the following (indentation added to stress the calling flow).
start start defineBlock start arg start evaluateBlock start block start start end
What we see is that the calling method start
is fully executed. The method defineBlock
is not completely executed. Indeed, its escaping block [^33]
is executed two calls away in the method evaluateBlock:
. The evaluation of the block returns to the block home context sender (i.e., the context that invoked the method creating the block).
When the return statement of the block is executed in the method evaluateBlock:
, the execution discards the pending computation and returns to the method execution point that created the home context of the block. The block is defined in the method defineBlock
. The home context of the block is the activation context that represents the definition of the method defineBlock
.
Therefore the return expression returns to the start
method execution just after the defineBlock
execution. This is why the pending executions of arg:
and evaluateBlock:
are discarded and why we see the execution of the method start
end.

As shown by Figure \(\PageIndex{1}\), [^33]
will return to the sender of its home context. [^33]
home context is the context that represents the execution of the method defineBlock
, therefore it will return its result to the method start
.
- Step 1 represents the execution up to the invocation of the method
defineBlock
. The trace'start start'
is printed. - Step 3 represents the execution up to the block creation, which is done in Step 2.
'defineBlock start'
is printed. The home context of the block is thedefineBlock
method execution context. - Step 4 represents the execution up to the invocation of the method
evaluateBlock:
.'arg start'
is printed. - Step 5 represents the execution up to the block evaluation.
'evaluateBlock start'
is printed. - Step 6 represents the execution of the block up to the condition:
'block start'
is printed. - Step 7 represents the execution up to the return statement.
- Step 8 represents the execution of the return statement. It returns to the sender of the block home context, i.e., just after the invocation of the method
defineBlock
in the methodstart
. The execution continues and'start end'
gets printed.
Non local return
[^ ...]
returns to the sender of the block home context, i.e., to the method execution point that called the one that created the block.
A return in a method returns a value to the sender of the method and stop executing the method containing the return. A non-local return does the same even if the block is executed by another method.
Accessing information. To manually verify and find the home context of a block we can do the following: Add the expression thisContext home inspect
in the block of the method defineBlock
. We can also add the expression thisContext closure home inspect
which accesses the closure via the current execution context and gets its home context. Note that in both cases, even if the block is evaluated during the execution of the method evaluateBlock:
, the home context of the block is the method defineBlock
.
Note that such expressions will be executed during the block evaluation.
Bexp>>defineBlock | res | self traceCr: 'defineBlock start'. res := self arg: [ thisContext home inspect. self traceCr: 'block start'. 1 isZero ifFalse: [ ^ 33 ]. self traceCr: 'block end'. ]. self traceCr: 'defineBlock end'. ^ res
To verify where the execution will end, you can use the expression thisContext home sender copy inspect.
which returns a method context pointing to the assignment in the method start
.
Couple more examples. The following examples show that escaping blocks jump to sender of their home contexts. The previous example shows that the method start
was fully executed. We define valuePassingEscapingBlock
on the class BlockClosure
as follows.
BlockClosure>>valuePassingEscapingBlock self value: [ ^nil ]
Then we define a simple assert:
method that raises an error if its argument is false.
Bexp>>assert: aBoolean aBoolean ifFalse: [Error signal]
We define the following method.
Bexp>>testValueWithExitBreak | val | [ :break | 1 to: 10 do: [ :i | val := i. i = 4 ifTrue: [ break value ] ] ] valuePassingEscapingBlock. val traceCr. self assert: val = 4.
This method defines a block whose argument break
is evaluated as soon as the step 4 of a loop is reached. A variable val
is then printed and we make sure that its value is 4. Executing Bexp new testValueWithExitBreak
performs without raising an error and prints 4 to the Transcript: the loop has been stopped, the value has been printed, and the assert has been validated.
If you change the valuePassingEscapingBlock
message sent by value: [^ nil]
in the testValueWithExitBreak
method above, you will not get the trace because the execution of the method testValueWithExitBreak
will exit when the block is evaluated. In this case, calling valuePassingEscapingBlock
is not equivalent to calling value: [^nil]
because the home context of the escaping block [ ^ nil ]
is different. With the original valuePassingEscapingBlock
, the home context of the block [^ nil]
is valuePassingEscapingBlock
and not the method testValueWithExitContinue
itself. Therefore when evaluated, the escaping block will change the execution flow to the valuePassingEscapingBlock
message in the method testValueWithExitBreak
(similarly to the previous example where the flow came back just after the invocation of the defineBlock
message). Put a self halt
before the assert:
to convince you. In one case, you will reach the halt while in another case not.
Non-local return blocks. As a block is always evaluated in its home context, it is possible to attempt to return from a method execution which has already returned. This runtime error condition is trapped by the VM.
Bexp>>returnBlock ^ [ ^ self ] Bexp new returnBlock value → Exception
When we execute returnBlock
, the method returns the block to its caller (here the top level execution). When evaluating the block, because the method defining it has already terminated and because the block is containing a return expression that should normally return to the sender of the block home context, an error is signaled.
Conclusion. Blocks with non-local expressions ([^ ...]
) return to the sender of the block home context (the context representing the execution led to the block creation).