Discussion:
Continuations and blocks
(too old to reply)
d***@googlemail.com
2012-03-15 14:26:55 UTC
Permalink
Hello everyone,

I've just started to learn Smalltalk and struggled over the concept of continuations.

As far as I understand continuations they are objects holding the full state of a process (including the full call-stack, the program counter and locale variables). When using such a continuation to resume a process, than the current process should be reset to the state described within the continuation object. I thought it would be obvious that the program counter would be reset as well to the next statement after the definition of the continuation: but it doesn't seem to work like this.

See the following example:

We have a class named "SomeClass" with an instance-method "doSomething", defined as follows:
doSomething: aBlock
aBlock value.
^'Hello from inside method'

Now we use the method in the workspace like this:
| returnValue |
returnValue := SomeClass1 new doSomething: [ Transcript show: 'Hello from closure'.
^'Some return value from the closure' ].
Transcript show: 'The method returned ' , returnValue.

If you execute the code within the workspace you will only see the single output:
Hello from Closure

Obviously the value 'Hello from inside method" will NOT be returned, because the return statement inside the closure will reset the process to the continuation-object state. But shouldn't this mean, that we should return to the next statement within the workspace, i.e. "Transcript show: 'The method returned' , returnValue.'?

Thanks for you help,

Dirk
Chris Uppal
2012-03-16 08:46:31 UTC
Permalink
Post by d***@googlemail.com
I've just started to learn Smalltalk and struggled over the concept of continuations.
You clearly understand what continuations are, but what I think you may be
missing is that Smalltalk blocks (or block closures) are not intended to create
continuations. All they do is create a /closure/. It may be that that bit of
rather over-technical (I think) jargon has mislead you.

When you run your workspace example, the code in the block is bundled up with
the values of any variables that are visible at that scope and saved in case
it's later executed. I.e. it's deferred. That idea of bundling up the
variables and the deferred code is called "closure" when there's a need to talk
about it at all. Of course, there aren't any interesting variables in your
example, only "returnValue" which isn't yet set, and which in any case isn't
used in the block.

When you later send #value to the block, the saved code gets executed. In your
example the block does a "non-local return" which will return FROM (not to) the
original context. Because your original context wasn't a real method call with
a real caller, there's nowhere special for it to return to, so (in this special
case of workspace code) nothing much happens except that the block finishes
executing.

Now there is a similarity of sorts between that (or rather, the underlying
mechanism which allows all that to work) and the concept of continuations, but
they aren't at all the same thing. As far as I know, you can build
continuations out of blocks and non-local returns, but the critical thing you
have to add (which you don't get in Smalltalk unless you change something deep)
is the ability to evaluate a block with a non-local return /twice/. And in
order to be able to support that, you need to add some way of preserving the
stack (copy it off to somewhere). In a normal non-local return the existing
stack is just unwound, and so any stack frames between the block creation and
the block evaluation are just lost. To be able to able to evaluate that block
a second time, we'd have had to have stored those frames somewhere and have
some way of restoring them and the associated execution state -- but /that/,
the bit that's missing, is (I think) the essence of continuations.

But on the whole, I don't think that the concept of continuations helps with
understanding Smalltalk blocks, and their rather unusual feature of non-local
return. I'd also add that creating blocks with non-local returns in a workspace
and doing stuff with them is likely to be confusing and/or misleading,
precisely because the code is executed in a workspace and therefore the
non-local return has nowhere to return to as it normally would.

Hope that helps rather than adds more confusion ;-)

-- chris
Dirk Weber
2012-03-17 21:48:24 UTC
Permalink
Hi Chris,
When you later send #value to the block, the saved code gets executed.  In your
example the block does a "non-local return" which will return FROM (not to) the
original context.  Because your original context wasn't a real method call with
a real caller, there's nowhere special for it to return to, so (in this special
case of workspace code) nothing much happens except that the block finishes
executing.
Thanks, I think this was the part I didn't get right before: as far as
I understand you the main-problem was that I executed the "print"-
closure directly within the workspace which was also the home-context
of the closure (-> definition AND execution of the closure resided
within one single "method"). Because the non-local-return simply exits
all surrounding closures and methods from the point of execution up to
the home-context-method it also returned immediately from the
workspace.
Hope that helps rather than adds more confusion ;-)
You helped me a lot. Thanks for you exlanations and have a nice
weekend,

Dirk

Loading...