Smalltalk: context cannot return
Asked Answered
C

1

5

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.

Cirrus answered 31/3, 2014 at 21:29 Comment(0)
K
8

You probably want to write

f := [ :x :y | x + y].
sum:=f value: 3 value: 6.

Why?

Let me tell you about

Non-local returns.

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 (!).

Why does it work in one go?

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.

Knighton answered 31/3, 2014 at 23:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.