New Local Variable Scope rule

Discussion in 'Ruby' started by Shashank Date, Apr 14, 2004.

  1. In one of Matz's slides at RubyConf ,

    (http://www.rubyist.net/~matz/slides/rc2003/mgp00012.html)

    he mentions his New Local Variable Scope rule as follows:

    #---------------------------------------------------------------
    def foo
    a = nil
    ary.each do |b|
    # b is block local
    c = b
    a = b
    # a and c are local to the method
    end
    # a and c available here
    end
    #---------------------------------------------------------------

    I was wondering what would happen if it was written as follows:

    #---------------------------------------------------------------
    def foo(&block)
    a=nil;
    ary.each(&block)
    end;

    foo(lambda {|b| a=b; c=b})
    #---------------------------------------------------------------

    In other words, in general, what is the advantage of having variables in
    closures leak out?
    -- shanko
    Shashank Date, Apr 14, 2004
    #1
    1. Advertising

  2. "Shashank Date" <> schrieb im Newsbeitrag
    news:c5id4r$22o3g$-berlin.de...
    > In one of Matz's slides at RubyConf ,
    >
    > (http://www.rubyist.net/~matz/slides/rc2003/mgp00012.html)
    >
    > he mentions his New Local Variable Scope rule as follows:
    >
    > #---------------------------------------------------------------
    > def foo
    > a = nil
    > ary.each do |b|
    > # b is block local
    > c = b
    > a = b
    > # a and c are local to the method
    > end
    > # a and c available here
    > end
    > #---------------------------------------------------------------
    >
    > I was wondering what would happen if it was written as follows:
    >
    > #---------------------------------------------------------------
    > def foo(&block)
    > a=nil;
    > ary.each(&block)
    > end;
    >
    > foo(lambda {|b| a=b; c=b})
    > #---------------------------------------------------------------


    First of all both examples are erroneous IMHO because ary is not defined
    in foo().

    > In other words, in general, what is the advantage of having variables in
    > closures leak out?


    Your second example will not modify the a defined in foo() because the
    binding takes place at the point of invocation, i.e. another scope. If
    you do

    def foo(&block)
    a=nil;
    ary.each(&block)
    puts "a=#{a.inspect}"
    end;

    foo(lambda {|b| a=b; c=b})
    puts "a*#{a.inspect}"

    You'll see
    a=nil
    a*<last enum element>

    Regards

    robert
    Robert Klemme, Apr 14, 2004
    #2
    1. Advertising

  3. Hi --

    "Robert Klemme" <> writes:

    > "Shashank Date" <> schrieb im Newsbeitrag
    > news:c5id4r$22o3g$-berlin.de...
    > > In one of Matz's slides at RubyConf ,
    > >
    > > (http://www.rubyist.net/~matz/slides/rc2003/mgp00012.html)
    > >
    > > he mentions his New Local Variable Scope rule as follows:
    > >
    > > #---------------------------------------------------------------
    > > def foo
    > > a = nil
    > > ary.each do |b|
    > > # b is block local
    > > c = b
    > > a = b
    > > # a and c are local to the method
    > > end
    > > # a and c available here
    > > end
    > > #---------------------------------------------------------------
    > >
    > > I was wondering what would happen if it was written as follows:
    > >
    > > #---------------------------------------------------------------
    > > def foo(&block)
    > > a=nil;
    > > ary.each(&block)
    > > end;
    > >
    > > foo(lambda {|b| a=b; c=b})
    > > #---------------------------------------------------------------

    >
    > First of all both examples are erroneous IMHO because ary is not defined
    > in foo().


    OK, OK... def ary; [1,2,3]; end :)

    > > In other words, in general, what is the advantage of having variables in
    > > closures leak out?

    >
    > Your second example will not modify the a defined in foo() because the
    > binding takes place at the point of invocation, i.e. another scope. If
    > you do


    I think you mean the point of creation (of the closure), rather than
    invocation?

    > def foo(&block)
    > a=nil;
    > ary.each(&block)
    > puts "a=#{a.inspect}"
    > end;
    >
    > foo(lambda {|b| a=b; c=b})
    > puts "a*#{a.inspect}"
    >
    > You'll see
    > a=nil
    > a*<last enum element>


    OK, but there's still 'c'. The new thing is the idea of a local
    variable created in a block and persisting after the block exits. If
    the scope that matters is where the block was created, then what
    happens in this case:

    def call_foo
    foo(lambda{|b| c=b})
    puts c # does c persist if the lambda was called?
    end


    David

    --
    David A. Black
    David Alan Black, Apr 14, 2004
    #3
  4. Shashank Date

    ts Guest

    >>>>> "D" == David Alan Black <> writes:

    D> def call_foo
    D> foo(lambda{|b| c=b})

    D> puts c # does c persist if the lambda was called?
    D> end

    What you have written is similar to

    def call_foo
    a = lambda {|b| c = b}
    foo(a)
    puts c
    end

    foo(a) don't change anything and if I've well understood : this is at
    compile time that ruby will make the decision (local/block-local)


    Guy Decoux
    ts, Apr 14, 2004
    #4
  5. Hi --

    ts <> writes:

    > >>>>> "D" == David Alan Black <> writes:

    >
    > D> def call_foo
    > D> foo(lambda{|b| c=b})
    >
    > D> puts c # does c persist if the lambda was called?
    > D> end
    >
    > What you have written is similar to
    >
    > def call_foo
    > a = lambda {|b| c = b}
    > foo(a)
    > puts c
    > end
    >
    > foo(a) don't change anything and if I've well understood : this is at
    > compile time that ruby will make the decision (local/block-local)


    OK... but what will the decision be, for 'c'? Why would it be any
    different here than for:

    def foo
    [1].each { c = 0 }
    puts c
    end

    In both cases, an assignment is being made to c, visible at compile
    time, in a particular scope. So, in your example (and mine), c is
    created at compile time in the calling scope (call_foo). Then the
    lambda is called, and c is actually assigned something.

    Or is there going to be a further distinction between plain lambdas
    and code blocks passed to iterators? If so, I really think it's time
    for a Block class. (Matz: you wanted more reasons.... :)


    David

    --
    David A. Black
    David Alan Black, Apr 14, 2004
    #5
  6. Shashank Date

    ts Guest

    >>>>> "D" == David Alan Black <> writes:

    D> OK... but what will the decision be, for 'c'? Why would it be any
    D> different here than for:

    Well, write your example like this

    def call_foo
    c = nil
    foo(lambda{|b| c=b})
    puts c
    end

    and you can test the result with 1.8 :)


    Guy Decoux
    ts, Apr 14, 2004
    #6
  7. "David Alan Black" <> schrieb im Newsbeitrag
    news:...
    > Hi --
    >
    > ts <> writes:
    >
    > > >>>>> "D" == David Alan Black <> writes:

    > >
    > > D> def call_foo
    > > D> foo(lambda{|b| c=b})
    > >
    > > D> puts c # does c persist if the lambda was

    called?
    > > D> end
    > >
    > > What you have written is similar to
    > >
    > > def call_foo
    > > a = lambda {|b| c = b}
    > > foo(a)
    > > puts c
    > > end
    > >
    > > foo(a) don't change anything and if I've well understood : this is at
    > > compile time that ruby will make the decision (local/block-local)


    Maybe the call should've read "foo &a".

    > OK... but what will the decision be, for 'c'? Why would it be any
    > different here than for:
    >
    > def foo
    > [1].each { c = 0 }
    > puts c
    > end
    >
    > In both cases, an assignment is being made to c, visible at compile
    > time, in a particular scope. So, in your example (and mine), c is
    > created at compile time in the calling scope (call_foo). Then the
    > lambda is called, and c is actually assigned something.


    Yes, of course. But there's a difference between

    def foo
    [1].each { c = 0 }
    puts c
    end

    and

    def foo(&b)
    [1].each &b
    puts c
    end
    foo { c = 0 }

    'c' comes into existence in the lexically surrounding scope, i.e. foo in
    the first case and the calling context in the second case. There is no
    magic that would make the second example print something other than 'nil'
    for 'c'.

    > Or is there going to be a further distinction between plain lambdas
    > and code blocks passed to iterators? If so, I really think it's time
    > for a Block class. (Matz: you wanted more reasons.... :)


    It would certainly be intersting to hear that. Up to now I thought
    'lambda' is merely a syntactical alias for 'proc' but I may be wrong here.

    Regards

    robert
    Robert Klemme, Apr 14, 2004
    #7
  8. Shashank Date

    ts Guest

    >>>>> "R" == Robert Klemme <> writes:

    R> def foo(&b)
    R> [1].each &b
    R> puts c
    R> end
    R> foo { c = 0 }

    R> 'c' comes into existence in the lexically surrounding scope, i.e. foo in
    R> the first case and the calling context in the second case. There is no
    R> magic that would make the second example print something other than 'nil'
    R> for 'c'.

    Well, it will just give

    in `foo': undefined local variable or method `c'


    :)


    Guy Decoux
    ts, Apr 14, 2004
    #8
  9. "ts" <> schrieb im Newsbeitrag
    news:...
    > >>>>> "R" == Robert Klemme <> writes:

    >
    > R> def foo(&b)
    > R> [1].each &b
    > R> puts c
    > R> end
    > R> foo { c = 0 }
    >
    > R> 'c' comes into existence in the lexically surrounding scope, i.e. foo

    in
    > R> the first case and the calling context in the second case. There is

    no
    > R> magic that would make the second example print something other than

    'nil'
    > R> for 'c'.
    >
    > Well, it will just give
    >
    > in `foo': undefined local variable or method `c'


    Exactly.

    robert
    Robert Klemme, Apr 14, 2004
    #9
  10. Hi --

    ts <> writes:

    > >>>>> "D" == David Alan Black <> writes:

    >
    > D> def call_foo
    > D> foo(lambda{|b| c=b})
    >
    > D> puts c # does c persist if the lambda was called?
    > D> end
    >
    > What you have written is similar to
    >
    > def call_foo
    > a = lambda {|b| c = b}
    > foo(a)
    > puts c
    > end
    >
    > foo(a) don't change anything and if I've well understood : this is at
    > compile time that ruby will make the decision (local/block-local)


    I'm really answering two of your posts, sort of -- this one, and the
    one where you give the 1.8 example with c=nil.

    I think foo(a) does change something, in the 2.0 model. If you don't
    have foo(a), then puts c will raise an error:

    def call_foo
    a = lambda {|b| c = b}
    puts c # error
    end

    This is therefore not the same as doing this in 1.8:

    def call_foo
    c = nil
    a = lambda {|b| c = b}
    puts c # OK, even if lambda never executes
    end


    David

    --
    David A. Black
    David Alan Black, Apr 14, 2004
    #10
  11. Hi --

    "Robert Klemme" <> writes:

    > "David Alan Black" <> schrieb im Newsbeitrag
    > news:...
    > > Hi --
    > >
    > > ts <> writes:
    > >
    > > > >>>>> "D" == David Alan Black <> writes:
    > > >
    > > > D> def call_foo
    > > > D> foo(lambda{|b| c=b})
    > > >
    > > > D> puts c # does c persist if the lambda was

    > called?
    > > > D> end
    > > >
    > > > What you have written is similar to
    > > >
    > > > def call_foo
    > > > a = lambda {|b| c = b}
    > > > foo(a)
    > > > puts c
    > > > end
    > > >
    > > > foo(a) don't change anything and if I've well understood : this is at
    > > > compile time that ruby will make the decision (local/block-local)

    >
    > Maybe the call should've read "foo &a".
    >
    > > OK... but what will the decision be, for 'c'? Why would it be any
    > > different here than for:
    > >
    > > def foo
    > > [1].each { c = 0 }
    > > puts c
    > > end
    > >
    > > In both cases, an assignment is being made to c, visible at compile
    > > time, in a particular scope. So, in your example (and mine), c is
    > > created at compile time in the calling scope (call_foo). Then the
    > > lambda is called, and c is actually assigned something.

    >
    > Yes, of course. But there's a difference between
    >
    > def foo
    > [1].each { c = 0 }
    > puts c
    > end
    >
    > and
    >
    > def foo(&b)
    > [1].each &b
    > puts c
    > end
    > foo { c = 0 }
    >
    > 'c' comes into existence in the lexically surrounding scope, i.e. foo in
    > the first case and the calling context in the second case. There is no
    > magic that would make the second example print something other than 'nil'
    > for 'c'.


    Right -- see my wording above ("c is created at compile time in the
    calling scope", which means foo in your first example and top-level in
    your second example, and call_foo in my call_foo example). I somehow
    had the impression Guy was saying that in the call_foo example, no 'c'
    would be created in call_foo but I now don't think he was. (That's
    what I meant when I asked why one would be different from the other.)

    I still find it odd what happens in that calling scope. As I
    understand it, after calling foo {|b| c = b}, you may or may not have
    a variable 'c', depending on the behavior of foo (i.e., whether foo
    does or does not call [or yield to] your block). To me, this
    particular kind of dependency between scopes is strange.

    > > Or is there going to be a further distinction between plain lambdas
    > > and code blocks passed to iterators? If so, I really think it's time
    > > for a Block class. (Matz: you wanted more reasons.... :)

    >
    > It would certainly be intersting to hear that. Up to now I thought
    > 'lambda' is merely a syntactical alias for 'proc' but I may be wrong here.


    No, you're right. ('proc' is deprecated in favor of lambda, to avoid
    the similarity in name between the non-similar Proc and proc objects.)
    But see ruby-core, and probably ruby-talk too in the past; there have
    been discussions about the various differences between and among Proc
    objects, lambdas, and code blocks. (I'm not going to tempt fate by
    trying to summarize them :)


    David

    --
    David A. Black
    David Alan Black, Apr 14, 2004
    #11
  12. Shashank Date

    ts Guest

    >>>>> "D" == David Alan Black <> writes:

    D> def call_foo
    D> a = lambda {|b| c = b}
    D> puts c # error
    D> end

    You want to say that this will give an error

    def foo(x)
    end

    def call_foo
    foo(lambda {|b| c = b})
    puts c # error
    end

    and this not ?

    def foo(x)
    x.call(24)
    end

    def call_foo
    foo(lambda {|b| c = b})
    puts c # 24
    end


    Guy Decoux
    ts, Apr 15, 2004
    #12
  13. Hi --

    ts <> writes:

    > >>>>> "D" == David Alan Black <> writes:

    >
    > D> def call_foo
    > D> a = lambda {|b| c = b}
    > D> puts c # error
    > D> end
    >
    > You want to say that this will give an error
    >
    > def foo(x)
    > end
    >
    > def call_foo
    > foo(lambda {|b| c = b})
    > puts c # error
    > end
    >
    > and this not ?
    >
    > def foo(x)
    > x.call(24)
    > end
    >
    > def call_foo
    > foo(lambda {|b| c = b})
    > puts c # 24
    > end


    Yes, that's my interpretation. The other possibility, I think, is
    that c would be nil in the first example, similar to the familiar case
    of:

    if false; a = 1; end
    p a # nil


    That may be what will happen:

    l = lambda { a = 1 }
    p a # nil (not error)

    though I find that also to be strange behavior for a closure.

    (Of course, I'm one of the few who think the way block variables work
    pre-2.0 is fine, but that's another story :)


    David

    --
    David A. Black
    David Alan Black, Apr 15, 2004
    #13
  14. Shashank Date

    ts Guest

    >>>>> "D" == David Alan Black <> writes:

    D> That may be what will happen:

    D> l = lambda { a = 1 }
    D> p a # nil (not error)

    Well, this is what I've trying to say

    l = lambda { a = 1 } # 2.0

    is similar to

    a = nil
    l = lambda { a = 1 } # 1.8

    D> (Of course, I'm one of the few who think the way block variables work
    D> pre-2.0 is fine, but that's another story :)

    1) confuse some persons, which see |a, b| as a declaration rather than an
    assignement

    2) there are really 2 different uses for block-local variable. In an
    iterator probably you'll expect the 2.0 behaviour, in a Thread (or
    perhaps a Proc) probably you expect the 1.8 behaviour


    Guy Decoux
    ts, Apr 15, 2004
    #14
  15. Hi --

    ts <> writes:

    > >>>>> "D" == David Alan Black <> writes:

    >
    > D> That may be what will happen:
    >
    > D> l = lambda { a = 1 }
    > D> p a # nil (not error)
    >
    > Well, this is what I've trying to say
    >
    > l = lambda { a = 1 } # 2.0
    >
    > is similar to
    >
    > a = nil
    > l = lambda { a = 1 } # 1.8


    I guess I've been trying to have it be something else because I
    dislike that so much (the 2.0 version).

    > D> (Of course, I'm one of the few who think the way block variables work
    > D> pre-2.0 is fine, but that's another story :)
    >
    > 1) confuse some persons, which see |a, b| as a declaration rather than an
    > assignement


    I wish they would just learn it :) But of course Matz has decided he
    doesn't like it, so that's that.

    > 2) there are really 2 different uses for block-local variable. In an
    > iterator probably you'll expect the 2.0 behaviour, in a Thread (or
    > perhaps a Proc) probably you expect the 1.8 behaviour


    That's why I was asking earlier whether this is a further difference
    between Procs and blocks. For example:

    # 2.0
    def x
    pr = Proc.new { a = 1 }
    l = lambda { b = 1 }
    # do we have a and b defined here, or just b?
    end

    Or when you say 'expect', do you mean 'expect, but not receive'? :)


    David

    --
    David A. Black
    David Alan Black, Apr 15, 2004
    #15
  16. Shashank Date

    ts Guest

    >>>>> "D" == David Alan Black <> writes:

    D> I guess I've been trying to have it be something else because I
    D> dislike that so much (the 2.0 version).

    Well, if I've well understtod you still have the possibility to write

    l = lambda { local {|a| a = 1} } # ugly ???

    D> pr = Proc.new { a = 1 }
    D> l = lambda { b = 1 }

    and what do you do with this ?

    Thread.new { a = 1 } # like Proc or like lambda ?
    NewClass.new { a = 1 } # like Proc or like lambda ?
    new_method { a = 1 } # like Proc or like lambda ?


    Guy Decoux
    ts, Apr 15, 2004
    #16
  17. Hi --

    ts <> writes:

    > >>>>> "D" == David Alan Black <> writes:

    >
    > D> I guess I've been trying to have it be something else because I
    > D> dislike that so much (the 2.0 version).
    >
    > Well, if I've well understtod you still have the possibility to write
    >
    > l = lambda { local {|a| a = 1} } # ugly ???


    I admit I'm confused by this. Is 'local' being introduced? And if
    |a| is a block variable, isn't it local to the block anyway?

    > D> pr = Proc.new { a = 1 }
    > D> l = lambda { b = 1 }
    >
    > and what do you do with this ?
    >
    > Thread.new { a = 1 } # like Proc or like lambda ?
    > NewClass.new { a = 1 } # like Proc or like lambda ?
    > new_method { a = 1 } # like Proc or like lambda ?


    I'm not sure what you mean. Do you mean what would I do if I were
    designing Ruby 2.0, or what do I think Matz will have them do? I
    don't know, since I still don't know the answer to the above (whether
    Proc and lambda will be different in this respect). I think maybe you
    think that I know, but I don't :)


    David

    --
    David A. Black
    David Alan Black, Apr 15, 2004
    #17
  18. Shashank Date

    ts Guest

    >>>>> "D" == David Alan Black <> writes:

    D> ts <> writes:

    D> I admit I'm confused by this. Is 'local' being introduced? And if
    D> |a| is a block variable, isn't it local to the block anyway?

    [ruby-talk:63199]

    >> and what do you do with this ?
    >>
    >> Thread.new { a = 1 } # like Proc or like lambda ?
    >> NewClass.new { a = 1 } # like Proc or like lambda ?
    >> new_method { a = 1 } # like Proc or like lambda ?


    D> I'm not sure what you mean. Do you mean what would I do if I were
    D> designing Ruby 2.0,

    yes,

    D> or what do I think Matz will have them do? I
    D> don't know, since I still don't know the answer to the above (whether
    D> Proc and lambda will be different in this respect). I think maybe you
    D> think that I know, but I don't :)

    If you introduce a difference between Proc and lambda, what do you do
    with the examples that I've given ?

    Thread will work like Proc or like lambda ?



    Guy Decoux
    ts, Apr 15, 2004
    #18
  19. On Thursday, April 15, 2004, 7:14:11 PM, David wrote:

    >> It would certainly be intersting to hear that. Up to now I thought
    >> 'lambda' is merely a syntactical alias for 'proc' but I may be wrong here.


    > No, you're right. ('proc' is deprecated in favor of lambda, to avoid
    > the similarity in name between the non-similar Proc and proc objects.)
    > But see ruby-core, and probably ruby-talk too in the past; there have
    > been discussions about the various differences between and among Proc
    > objects, lambdas, and code blocks. (I'm not going to tempt fate by
    > trying to summarize them :)



    I wish someone would summarize them. The inevitable difficulty of
    doing so would demonstrate what to my mind is obvious: the complexity
    is getting way out of hand!

    I've forgotten what the motivation for splitting apart a seemingly
    unified concept is.

    Gavin
    Gavin Sinclair, Apr 15, 2004
    #19
  20. "David Alan Black" <> schrieb im Newsbeitrag
    news:...
    > Hi --
    >
    > ts <> writes:
    >
    > > >>>>> "D" == David Alan Black <> writes:

    > >
    > > D> That may be what will happen:
    > >
    > > D> l = lambda { a = 1 }
    > > D> p a # nil (not error)
    > >
    > > Well, this is what I've trying to say
    > >
    > > l = lambda { a = 1 } # 2.0
    > >
    > > is similar to
    > >
    > > a = nil
    > > l = lambda { a = 1 } # 1.8

    >
    > I guess I've been trying to have it be something else because I
    > dislike that so much (the 2.0 version).
    >
    > > D> (Of course, I'm one of the few who think the way block variables

    work
    > > D> pre-2.0 is fine, but that's another story :)
    > >
    > > 1) confuse some persons, which see |a, b| as a declaration rather

    than an
    > > assignement

    >
    > I wish they would just learn it :) But of course Matz has decided he
    > doesn't like it, so that's that.
    >
    > > 2) there are really 2 different uses for block-local variable. In an
    > > iterator probably you'll expect the 2.0 behaviour, in a Thread

    (or
    > > perhaps a Proc) probably you expect the 1.8 behaviour

    >
    > That's why I was asking earlier whether this is a further difference
    > between Procs and blocks. For example:
    >
    > # 2.0
    > def x
    > pr = Proc.new { a = 1 }
    > l = lambda { b = 1 }
    > # do we have a and b defined here, or just b?
    > end


    Why would we have anything defined here although both blocks are not
    executed in this example? Slowly I'm loosing grips here... A summary was
    definitely in order.

    I used to be confused, but now I'm not sure anymore.

    robert
    Robert Klemme, Apr 15, 2004
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. pembed2003

    scope and linkage rule, very confusing!

    pembed2003, May 26, 2004, in forum: C Programming
    Replies:
    5
    Views:
    664
    Jack Klein
    May 27, 2004
  2. Antoon Pardon

    Scope rule pecularities

    Antoon Pardon, May 6, 2004, in forum: Python
    Replies:
    39
    Views:
    717
    Josiah Carlson
    May 20, 2004
  3. Replies:
    0
    Views:
    1,358
  4. David Filmer
    Replies:
    19
    Views:
    240
    Kevin Collins
    May 21, 2004
  5. Andrew Falanga
    Replies:
    2
    Views:
    201
    Andrew Falanga
    Nov 22, 2008
Loading...

Share This Page