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
|