From: "Henry T. So Jr." Date: 2001-03-31T07:52:12+09:00 Subject: [ruby-talk:13367] Re: f.call (x) revisited In "Doug Edmunds" [ruby-talk:13363], wrote: > > a = 1 ; x = 1 ; f = proc {|a| a += 1} > > f.call(x) > > f.call(a) > > a # results in 2 << > > a = 1 ; x = 1 ; f = proc {|a| a += 1} > > f.call(a) > > f.call(x) > >a # results in 3 << > I decided to try out his example. I didn't get his results. > I got 3 for the first result and 2 for the second. > I have expanded the examples to try to see what is going on. > I leave it to the group to explain: This is all basically due to the scoping in Ruby. By the time the closure is encountered, the variable a has already been established as having scope outside the block. In Ruby, the scope of local variables (that is, those variables without punctuation as in $a, @a, or @@a) inside blocks is such that any variables in existence before the block is encountered maintain their existence (and scope), and any variables not in existence before the block is encountered are local to the block (even if they are made to exist after the block is encountered). > a) why f.call(x) is always 2 By calling f.call(x), you are calling f.call(1). The "|a|" part of the block makes the implicit assignment "a = 1". This alters the value of a outside the block since the scope of a is outside the block. Then the block is executed and a is incremented. Thus, a, after calling the closure, has a value of 2 (and will always have a value of 2 because x was 1). > b) why f.call(a) is always 3 after f.call(x) Continuing from the above statement, after f.call(x), a has a value of 2. When you then call f.call(a), you are calling f.call(2). The "|a|" part of the block makes the implicit assignment "a = 2". This alters the value of a outside the block (although a was already 2, so there is no apparent change). When the block is executed, a is incremented, resulting in the value 3. > c) where the value of a is coming from in proc {|a| a += 1} a is assigned to upon entering the block because of the |a| declaration. This block has the side effect (in this example) of altering the a outside the block because the scope of a has been established outside the block (and in fact, the a inside the block is the a outside the block). Consider this: a = 1; x = 1; f = proc {|c| c += 1} f.call(a) # => 2 f.call(x) # => 2 a # => 1 x # => 1 c # => not defined and this a = 1; x = 1; c = 1; f = proc {|c| c += 1} f.call(a) # => 2 (same as c = a; c += 1) f.call(x) # => 2 (same as c = x; c += 1) a # => 1 x # => 1 c # => 2 In the first example, c is not defined before the closure is encountered, so changing c in the closure has no effect outside the closure. In the second example, c is defined before the closure is encountered, so changing c in the closure has the effect of altering the c outside the closure. The important thing to remember is that blocks and closures are not the same as methods. They, by definition, have an effect based on the "environment" in which they were encountered. Methods, on the other hand, are more independent of the "environment" in which they are encountered. Understanding this may be one of the steps to fully appreciating Ruby's "harmony". Cheers, Henry.