The following Smalltalk code returns the error "context cannot return" if I execute them one by one. Anyone has an explanation please?
f := [ :x :y | ^x + y].
sum:= f value: 3 value: 6.
If I execute them at one go, it works and returns 9
as expected.
You probably want to write
f := [ :x :y | x + y].
sum:=f value: 3 value: 6.
Let me tell you about
When you want to return something from a method, you use the ^
caret in a method to do so:
doSomethingUseful
^ self calculate + 1
This is a normal return. Everything ok. Now enter blocks.
You always implicitly return something from a block, the value of its last expression.
So, this block would return 42
, when executed:
[1 + someObject invert.
anotherObject * 4.
42].
You can use that in methods:
doSomethingUseful: someObject to: anotherObject
| myValue |
myValue := [1 + someObject invert.
anotherObject * 4.
42] value.
^ self calculate + myValue
However, sometimes you have to return from the block out of the function. A typical example are guard clauses like this:
doThis
self someValueSatisfied ifFalse: [^ self]
self calculate.
^ self someValueComputed.
If #someValueSatsified
returns false
, the method immediately returns and never executes #calculate
or #someValueComputed
. This effect is called a non-local return because you return from the block not to its calling context (which would be local) but form the method it is defined in (!).
This is due to the way “Do-its” are handled in, eg. Squeak or Pharo. When you hit Ctrl-D (or equivalent), the currently selected code is secretly compiled as a method (well, there happens a little bit more, but lets ignore that). You can see that if you execute 1 halt
and look at the debugger.
So executing the code line-wise would do the following:
DoIt
f := [ :x :y | ^(x+y). ]. "! f is now defined in the work space"
DoIt
sum:=f value: 3 value: 6.
First, the block is created and stored somewhere in your workspace as f
. Then this do-it exits and the next is executed. Smalltalk finds f
in your workspace, being the stored block. It tries to execute it and encounters the non-local return. However, the non-local return only returns from the defining function, which is no longer executed and so we cannot return from it.
When you execute everything in one go, this would be like:
DoIt
f := [ :x :y | ^(x+y). ].
sum:=f value: 3 value: 6.
Nearly the same as above, except that now when f
is executed, the non-local return can work, as we still are in the function that defined the block. So we can return from it. The non-local return works in this case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With