Who wants full block closures and why?

Alan Lovejoy sourcery at pacbell.net
Sun Jul 18 00:20:15 UTC 1999


> ** Original Sender: "David N. Smith \(IBM\)" <dnsmith at watson.ibm.com>
> At 16:29 -0700 7/15/99, Craig Latta wrote:
> >	I'm compiling a summary for spec/documentation purposes. (I 
> >also intend to implement them :)
> 
> I do! Why?
> 
> Because I use them all the time in other implementations.

Yes. So do I.

> Because they allow optimized blocks: you can declare local variables 
> inside the block, not reference anything outside the block, and not 
> have to save a context. (And even if you *do* save the context you 
> don't have problems with multiple blocks referencing the same local 
> variable.)

This does not require closure semantics.  It just requires that blocks
have naming scopes and activation frames independent of the ones for 
the containing method.  

> Because blocks that are not closures are ugly kludges.

Yeah, but these things are relative.  Squeak's blocks are elegant compared
to, say, Java inner classes :-).

> Because you can pass optimized closures here and there and everywhere 
> else and hot have to worry about conflicts with single copy local 
> variables in the context.

Yes.

> Closures allow implementation of recursive blocks, and, yes indeed, 
> they are useful. Beside, why should the worlds best language have 
> such an ugly restriction?

Again, recursion only requires that each execution of a block have
a separate activation frame (e.g. in Smalltalk lingo, a separate Context).  

> Because, these days, it's not Smalltalk unless it has closures. (I 
> guess I must feel strongly about this!  :-)

Yes.  We're getting ANSI for closures! :-).

> (As a non-implementer, maybe I'm giving closures properties that are 
> not properly due to closures, but to other problems with blocks; 
> regardless, they are broken!)

Yes. Yes. Yes.

> Non-cute example:
> 
> | blockArray dataArray |
> blockArray := Array new: 5.
> dataArray := #( 'Apple' 'Orange' 'Grape' 'Lemon' 'Kiwi').
> 1 to: blockArray size do: [ :index |
> 	blockArray
> 		at: index
> 		put: [ dataArray at: index ] ].
> ^ (blockArray at: 2) value
> 
> In Squeak 2.4c it gets an index out of range error. (Yes, indeed! See 
> if you can figure out why before running it!)

Because the value of index (whose storage scope is that of the containing method) 
will be 6 when the block "[dataArray at: index]" actually executes.  This highlights
the semantic non-symmetry created by blocks that are not closures: unlike methods,
their arguments and temporaray variables do not have a storage location that differs
with each execution.

> Interesting to note that VW 2.5 (my most recent version) answers 
> 'Orange', while IBM/ST 4.5 answers 'Kiwi'.
> 
> There seems to be some disagreement about when variables in a context 
> are bound to the block. While 'Orange' is the answer I intended, I 
> find the IBM/ST result more understandable since there is no tricky 
> binding. (But, maybe I'm just confused!) To get an answer of 'Orange' 
> in IBM/ST it is necessary to write the #at:put: this way:
> 
> | blockArray dataArray |
> blockArray := Array new: 5.
> dataArray := #( 'Apple' 'Orange' 'Grape' 'Lemon' 'Kiwi').
> 1 to: blockArray size do: [ :index |
> 	blockArray
> 		at: index
> 		put: ([ :n |
> 			[ dataArray at: n ]
> 				] value: index) ].
> ^ (blockArray at: 2) value
> 
> At the time the #at:put: is executed, a block is evaluated, passing 
> index, and answering another block which has the value of index, but 
> not a direct reference to index.
> 
> 
> I don't particularity care which way it works, provided that there 
> are simple rules that describe it, and so that one is not terribly 
> surprised by the results. I find the VW result confusing after some 
> thought since it appears to reference a local variable, but doesn't; 
> I find the IBM/ST code less than obvious at first, but quite clear 
> once I've thought about it a while.
> 
> So, how should it be?

The VA behavior implies that it interprets block arguments (and perhaps
also temporaries?) as what C would call "static variables".  That is,
different executions of the same block all share the same storage
location for these variables.  Your second example works by defining
a new block for each loop of the outer block, thus defining a new activation
frame.  The outer block only has one activation frame, because it is
only defined once (apparently, each invocation of the containing context 
defines a new activation frame for the block).

The VA semantics does not satisfy the formal definition of closure.  It
also makes the semantics of code depend on whether it is encapsulated
as a block or a method (methods don't have "static" variables).

I vote for real closures.

--Alan



Download Neoplanet at http://www.neoplanet.com





More information about the Squeak-dev mailing list