Full Block Closure

Ian Piumarta Ian.Piumarta at inria.fr
Thu Nov 18 18:25:15 UTC 1999


> I keep hearing the need for "full block closure" in Squeak, what
> exactly is meant by this?

A closure is like a block: it hangs onto some state and some
functionality, and you can "run" it at an arbitrary time in the
future, as many times as you like.  Like blocks, they "capture"
(encapsulate, or "close-over" [whence the name]) their "free" state
(that which is "imported" from the defining context) when created.  If
you run a closure multiple times, you see the same "captured" state.
Unlike blocks, a closure gets different *local* state each time it's
run.  Its arguments and local variables, for example, are bound (to
use Lispy words) to different storage cells in the machine's memory
each time it is run.  If you arrange for that local state to be
accessible from outside, you don't get conflicts from state that
"escaped" from different invocations of the defining closure.

Blocks in Squeak are like closures: they capture their closed-over
state and give you back the same state each time they're run.  Unlike
closures, they don't get fresh *local* state when they're run.  This
state is actually part of the MethodContext in which the block is
created, so letting any of it escape from multiple invocations of a
given block will not work the way you expect.  Multiple invocations
that apparently close-over different local state will in fact share
the same state.

Clear as mud, no?  Example...

	| mkCounter b1 b2 |
	mkCounter _ [:init | [init _ init + 1]].

	"mkCounter is a block that makes other blocks that count
	 when evaluated...  init is the local state of the outer
	 block that is captured by the inner blocks and then made
	 accesible by sending them the #value message..."

	b1 _ mkCounter value: 42.
	Transcript print: b1 value; cr; endEntry.	"43"
	Transcript print: b1 value; cr; endEntry.	"44"

	"Correct: b1 closed-over the init from mkCounter and
	 gave back the same (modified) state each time we
	 sent it #value.  Lets make another one..."

	b2 _ mkCounter value: 666.
	Transcript print: b2 value; cr; endEntry.	"667"
	Transcript print: b2 value; cr; endEntry.	"668"

	"So far so good; now go back to the first..."

	Transcript print: b1 value; cr; endEntry.	"45?"
	Transcript print: b1 value; cr; endEntry.	"46?"

	"Argh!!!  b2 has trampled all over the closed-over
	 state of b1!!!"

In Squeak it's the argument :init that is stored in the MethodContext
in which all of the above blocks were defined, and not in the blocks
themselves (contrary to what you'd expect from the syntax).  Since the
blocks share their defining context, their local state is also shared.

See SortedCollection>>sortBlock: and the method that it calls
(BlockContext>>fixTemps) for an example of how this impacts everyday
life.

Someone else could probably explain all that in a much clearer fashion
than I just didn't...  The hardcore Scheme fanatics could even explain
why full closures are *mathematically* "sound", compared to blocks in
Squeak.

Ian





More information about the Squeak-dev mailing list