When is a closure a real one? [was: Real closures]

Marcus Denker denker at iam.unibe.ch
Mon Oct 9 10:13:15 UTC 2006


On 08.10.2006, at 10:51, Klaus D. Witzel wrote:

> Thank you Mathieu and Phillipe for you pointers and example.
>
> Just out of curiosity (and as input for writing accurate yes/no  
> test cases :) let me ask what is expected by the community  
> (apologies if this sounds like a silly question ;-) when is a  
> closure a real one:
>
> 1] after #fixTemps (or equivalent)

no, fixtemps just copies the home context.

Here is a quote from a mail that I send someone to explain this:

==============

This brings some of the properties of a closure, but not all. In a  
way, it's too much.

blocks1 := (1 to: 10) collect: [:ea | [ea]].
blocks1 collect: [:each | each value]

If you execute this in VW, you get #(1 2 3 4 5 6 7 8 9 10). But in
Squeak, the :ea
is just a temp in the method, so the blocks all reference the same
variable. Thus you get  #(10 10 10 10 10 10 10 10 10 10)

Now that is sometimes really not what you like, so they implemented a
hack (they are good at that!) called "fixTemps":

blocks1 := (1 to: 10) collect: [:ea | [ea] fixTemps].
blocks1 collect: [:each | each value]

fixTemps
        "Fix the values of the temporary variables used in the block  
that are
        ordinarily shared with the method in which the block is  
defined."

        home _ home copy.
        home swapSender: nil


Of course, this does not give you Closure semantics, too, as this now
makes one new environment per block for all temps of the block...
whereas with
Closures, this is not always the case, e.g. when referencing outer
temps:

|a|
a := 1.
b1 := [a].
b2 := [a] fixTemps.
a := 3.
b1 value + b2 value

is Wrong: The fixTemps makes the a in b2 it's own variable, but even
with Closures, it's the a of the method itself.

Another aspect is that Block Locals like [ | a | ] are from the code
that is generated indistiguishable from having the | a | defined as
temps of the method.

myMethod
    | a |
    [a].

same as

myMethod
     [ | a | a]

Some versions ago, Lex Spoon at least fixed the compiler to not
allow you to reference the block locals and arguments outside the
block. which was perfectly ok before. But nevertheless, the semantics
of scoping is Wrong:

tt2
        | a b |
        a := [ | t | t := 1].
        b := [ | t | t ].
        a value.
        ^b value.

returns 1, even if each block defines their own (new, completely  
different) t.

===================

>
> 2] after #blockCopy: (is this equivalent to 1?)
>

blockCopy: is the way to do generate BlockContexts in Squeak. No  
Clusures.
Not equivalent to 1).

blockCopy: numArgs
	"Primitive. Distinguish a block of code from its enclosing method by
	creating a new BlockContext for that block. The compiler inserts  
into all
	methods that contain blocks the bytecodes to send the message
	blockCopy:. Do not use blockCopy: in code that you write! Only the
	compiler can decide to send the message blockCopy:. Fail if numArgs is
	not a SmallInteger. Optional. No Lookup. See Object documentation
	whatIsAPrimitive."

	<primitive: 80>
	^ (BlockContext newForMethod: self home method)
		home: self home
		startpc: pc + 2
		nargs: numArgs


> 3] after #createBlock: (is this equivalent to 1? to 2?)
>

This gets you a real closure block (this is from the newcompiler's  
runtime):

createBlock: env

	^ BlockClosure new
		env: env;
		method: self


So: old compiler generates code with 2, people add 1 by hand  
(randomly, as
bugs show up). Newcompiler generates code with 3, 1 is not needed  
anymore.


      Marcus





More information about the Squeak-dev mailing list