Search code examples
smalltalksqueak

Block uses an outer variable in Squeak


In some myClass class I have:

foo
   |i b|
   i := 5.
   b := [i := i * 2. i].
   i :=3.
   ^b

I run:

|a i b1 b2|
i := 4.
a := MyClass new.
b1 := a foo.
b2 := a foo.
Transcript show: b1 value; cr.
i := 1.
Transcript show: b1 value; cr.
Transcript show: b2 value; cr.

I don't understand why the output is:

6
12
6

Is it possible to explain how the compiler figures it out?


Solution

  • What you get is the expected behavior. First of all the temporary i defined in the script, regardless of its name, has no influence or relationship whatsoever with temporary i in the #foo method, so you can get rid of it from the script.

    Secondly, the method answers with a BlockClosure, namely [i := i*2. i]. Which means that it will remember the value of i, double it, and then return it. As a side effect, the method also sets the value of i to 3.

    Then, when you evaluate b for the first time, i starts at 3, is doubled to 6 and then returned. Hence, you get 6. The second evaluation just evaluates the block, so it uses the current value of i, which is 6, doubles it and answers with the result, which is 12. Evaluate b again and you will get 24, 48, etc.

    Also note that every time you send #foo, the block it answers will evaluate to 6.

    Appendix

    The question mentions the compiler. Well, the compiler does very little in the script. In #foo, however, it does something more sophisticated, it creates the BlockClosure with code [i := i * 2. i]. Temporaries not used in blocks live in the stack, but this temporary i does not. Why? Because the block is an object that will survive the execution of the method, and the stack is gone as soon as the method returns. The block will then allocate the variable i in an Array that represents the environment the block needs to keep working. This Array is not allocated in the stack but in the object memory. This is where the block remembers the last value of i, and can update and use it as many times as needed even after the invokation of #foo, along with the stack it used gets ovewritten by other invocations. (And yes, in Smalltalk the compiler is a first class object too.)