ruby1.9 block scope

Discussion in 'Ruby' started by Daniel DeLorme, Sep 29, 2008.

  1. I could have sworn that one of the firm changes in ruby 1.9 was that
    variables defined inside a block would also exist outside the block. But
    when I try it:

    >> 1.times{ x=2 }

    => 1
    >> x

    NameError: undefined local variable or method `x' for main:Object

    Did I dream all that? If it wasn't a dream, when and why was that change
    reversed?

    Daniel
     
    Daniel DeLorme, Sep 29, 2008
    #1
    1. Advertising

  2. Daniel DeLorme

    James Coglan Guest

    [Note: parts of this message were removed to make it a legal post.]

    >
    > I could have sworn that one of the firm changes in ruby 1.9 was that
    > variables defined inside a block would also exist outside the block



    No, it's the reverse. Take this:

    x = 5
    1.upto(10) { |i| x = i }
    puts x

    ruby1.8 prints "10", ruby1.9 should print "5".
     
    James Coglan, Sep 29, 2008
    #2
    1. Advertising

  3. James Coglan wrote:
    >> I could have sworn that one of the firm changes in ruby 1.9 was that
    >> variables defined inside a block would also exist outside the block

    >
    >
    > No, it's the reverse. Take this:
    >
    > x = 5
    > 1.upto(10) { |i| x = i }
    > puts x
    >
    > ruby1.8 prints "10", ruby1.9 should print "5".


    Actually you are confusing things a litte. In this case ruby1.9 prints
    "10". Only the block *parameters* are local to the block:
    >> i = "a"

    => "a"
    >> 2.times{ |i| puts i }

    0
    1
    => 2
    >> puts i

    a

    Which is nice, but not nearly as nice as having the variables assigned
    inside the block available outside of it. These slides are quite old but
    this was the original plan:
    http://www.rubyist.net/~matz/slides/rc2003/mgp00012.html

    And I seem to remember I experienced that behavior in 1.9 a few months
    ago only. But not any more :-(

    --
    Daniel
     
    Daniel DeLorme, Sep 29, 2008
    #3
  4. Daniel DeLorme

    Lex Williams Guest

    Daniel DeLorme wrote:
    > Yukihiro Matsumoto wrote:
    >> upgraded to the level of the reference.

    > Do you mean upgrade the scope of the variable dynamically? Is that even
    > possible? I thought that all local vars had their scope pretty much set
    > in stone at the lexical level. But if you could upgrade their scoping
    > level dynamically... that would be nifty indeed!


    If he did that , we wouldn't have locals anymore .
    --
    Posted via http://www.ruby-forum.com/.
     
    Lex Williams, Sep 29, 2008
    #4
  5. Daniel DeLorme

    Mike Gold Guest

    Yukihiro Matsumoto wrote:
    >
    > As Nobu stated, you can explicitly declare block local variables,
    > using ';'. Besides that above code would not share variable x, since
    > the variable is not used upper level. They are two distinct
    > variables at the same level, with a same name. See the following
    > code, that makes a variables shared among blocks:
    >
    > class Foo
    > defined_method :bar do
    > x = 1
    > end
    > defined_method :baz do
    > x = 2
    > end
    > x = 45 # this assignment would make x shared with foo and bar under
    > the new rule.
    > end
    >
    > The reason I haven't introduced it yet in Ruby is that single
    > assignment after the blocks can change the scope of a variable
    > afterward. Same thing happens assignments _before_ the blocks
    > already. But I hesitated to enhance that far.
    >
    > matz.


    Making the scope of x contingent upon what comes later would result, I
    believe, in extreme confusion.

    Let's say Foo is a large class. Joe Schmo, who did not write Foo, comes
    along and adds some code to the bottom, such as the 'x = 45' in your
    example. Suddenly there is a bug in Foo. It seems impossible to Joe,
    but there it is. He wonders if it is caused by the recent sunspots. He
    wraps his computer in tin foil, but to no avail. The tests for bar and
    baz still fail as a result of his (obviously, to him) unrelated change.

    This was the 'tall' case, but there is also the 'wide' case of deeply
    nested scopes. An equal helping of confusion applies here too.

    One might argue that when 'x = 45' appears before bar and baz, this
    _already_ changes the meaning of bar and baz. But the situation here is
    entirely different, in my mind. Don't most of us read code from top to
    bottom?

    --
    Posted via http://www.ruby-forum.com/.
     
    Mike Gold, Sep 30, 2008
    #5
  6. Daniel DeLorme

    Mike Gold Guest

    Yukihiro Matsumoto wrote:
    > Hi,
    >
    > In message "Re: ruby1.9 block scope"
    > on Tue, 30 Sep 2008 16:49:19 +0900, Mike Gold
    > <> writes:
    >
    > |Making the scope of x contingent upon what comes later would result, I
    > |believe, in extreme confusion.
    >
    > |One might argue that when 'x = 45' appears before bar and baz, this
    > |_already_ changes the meaning of bar and baz. But the situation here is
    > |entirely different, in my mind. Don't most of us read code from top to
    > |bottom?
    >
    > Understandable. That's the very reason the current Ruby does not work
    > like this.
    >
    > matz.


    The current ruby doesn't, but a future one might? The reason I posted
    was because the phrase "I haven't introduced it yet" might imply that it
    could happen.
    --
    Posted via http://www.ruby-forum.com/.
     
    Mike Gold, Sep 30, 2008
    #6
  7. Mike Gold wrote:
    > Making the scope of x contingent upon what comes later would result, I
    > believe, in extreme confusion.
    >
    > Let's say Foo is a large class. Joe Schmo, who did not write Foo, comes
    > along and adds some code to the bottom, such as the 'x = 45' in your
    > example. Suddenly there is a bug in Foo. It seems impossible to Joe,
    > but there it is. He wonders if it is caused by the recent sunspots. He
    > wraps his computer in tin foil, but to no avail. The tests for bar and
    > baz still fail as a result of his (obviously, to him) unrelated change.
    >
    > This was the 'tall' case, but there is also the 'wide' case of deeply
    > nested scopes. An equal helping of confusion applies here too.
    >
    > One might argue that when 'x = 45' appears before bar and baz, this
    > _already_ changes the meaning of bar and baz. But the situation here is
    > entirely different, in my mind. Don't most of us read code from top to
    > bottom?


    Yes, I would argue that, and I believe most of us don't *add* new code
    always at the bottom of the file. If the bar and baz methods are defined
    at the bottom and Joe Schmo adds x = 45 at the top, he has the same
    problem. I really don't see how it's any different; it's symmetric. Bugs
    happen, and it's possible to come up with "problems cases" for
    absolutely every feature in ruby, but that doesn't mean they turn out to
    be problems in reality.

    I must ask: is this confusing to *you* or are you trying to "protect" a
    hypothetical Joe Schmo? I'll take the hypothetically confused Joe Schmo
    over the very *real* confusion of every ruby newbie who wonders why his
    local var has disappeared after the loop.

    --
    Daniel
     
    Daniel DeLorme, Sep 30, 2008
    #7
  8. Daniel DeLorme

    Mike Gold Guest

    Daniel DeLorme wrote:
    > Mike Gold wrote:
    >> This was the 'tall' case, but there is also the 'wide' case of deeply
    >> nested scopes. An equal helping of confusion applies here too.
    >>
    >> One might argue that when 'x = 45' appears before bar and baz, this
    >> _already_ changes the meaning of bar and baz. But the situation here is
    >> entirely different, in my mind. Don't most of us read code from top to
    >> bottom?

    >
    > Yes, I would argue that, and I believe most of us don't *add* new code
    > always at the bottom of the file. If the bar and baz methods are defined
    > at the bottom and Joe Schmo adds x = 45 at the top, he has the same
    > problem. I really don't see how it's any different; it's symmetric. Bugs
    > happen, and it's possible to come up with "problems cases" for
    > absolutely every feature in ruby, but that doesn't mean they turn out to
    > be problems in reality.
    >
    > I must ask: is this confusing to *you* or are you trying to "protect" a
    > hypothetical Joe Schmo? I'll take the hypothetically confused Joe Schmo
    > over the very *real* confusion of every ruby newbie who wonders why his
    > local var has disappeared after the loop.


    Joe Schmo is all of us. Like D. Black says, all of us would need to
    read the code twice before understanding it. In practice it is probably
    more common to read with backtracking, which is even slower.

    On the other hand, a ruby newbie who first encounters the rules for
    local variable bindings (in the form of an error) should in no manner be
    shielded from understanding them. That error is an important learning
    experience.

    For old-timers like me who, coming from lisp, often use closures to
    preserve state in conjunction with define_method on anonymous classes,
    the impact of the proposed change is significant (or unmanageable,
    depending on your point of view). It transforms code complexity from
    O(n)--n nested blocks--to O(n^2)--n nested blocks (which can't even be
    called "nested" anymore) with backtracking.
    --
    Posted via http://www.ruby-forum.com/.
     
    Mike Gold, Sep 30, 2008
    #8
  9. Daniel DeLorme

    Mike Gold Guest

    Daniel DeLorme wrote:
    > David A. Black wrote:
    >> before you could begin to understand any of it. And you would have to
    >> do that for *every* block, just in case, even if only 1% of them had a
    >> variable defined later.

    >
    > You dont need a two-pass visual scan; it just allows you to assume that
    > a variable defined a line L of a method will be available for reference
    > at line L+n, no matter if it was defined inside a block or not. It
    > should be common sense, really.


    You have described exactly what D. Black and I mean by a two-pass scan.
    Line L is dependent on code arbitrarily far ahead at L+n.

    When you encounter the variable at line L+n, you should backtrack to
    line L to determine whether or not the block scopes are aligned in such
    a way to change the meaning of line L.
    --
    Posted via http://www.ruby-forum.com/.
     
    Mike Gold, Sep 30, 2008
    #9
  10. Daniel DeLorme

    Mike Gold Guest

    I hope I'm not belaboring the point, but --

    a = Class.new {
    define_method:)f) {
    x
    }

    define_method:)x) {
    33
    }
    }.new

    x = 44

    b = Class.new {
    define_method:)f) {
    x
    }

    define_method:)x) {
    55
    }
    }.new

    p a.f # => 33
    p b.f # => 44

    You want a.f == 44. I do not.

    If you think this is a contrived example then you haven't been exposed
    to the elegant solution this gives for certain cases. At run-time we
    are creating additional layers of abstraction which translate the
    current context into pieces which are passed to lower levels.

    --
    Posted via http://www.ruby-forum.com/.
     
    Mike Gold, Sep 30, 2008
    #10
  11. Daniel DeLorme

    Mike Gold Guest

    Yukihiro Matsumoto wrote:
    >
    > Yes, but
    >
    > * Class.new and define_method is a rather rare examples, of which
    > non-block counterpart introduce new scopes.


    But it's not a rare example for me. It's a great solution to an
    otherwise tangled problem. When I have more time I can post some code
    if my example is not convincing.

    The whole point is that I don't want to introduce a new scope. As I
    briefly mentioned, I am writing adapters which put a "new face" on the
    current binding so that other levels can understand it.

    By contrast, it would be convoluted to create all new scopes each with
    instance variables pointing back to the places I just came from. The
    code would be Java-like: reams of scaffolding which serve no end except
    to compensate for a missing language feature.

    > * In 1.9, you can have explicit block local variables if you expect
    > any confusion.


    I would use the block local syntax every time, because I would always
    expect confusion. The ruby "good feeling" would be gone, for me.
    Effectively it would be Lisp "let" constructs, which is fine I guess,
    but it's Lisp.

    I have always thought of ruby as a (non-defmacro) Lisp "optimized" to
    make programmers more happy. In this case I feel the proposed
    "optimization" goes too far, producing unexpected behavior like in the
    example I gave. But I realize POLS refers to your surprise, not my
    surprise.
    --
    Posted via http://www.ruby-forum.com/.
     
    Mike Gold, Sep 30, 2008
    #11
  12. Hi,

    At Wed, 1 Oct 2008 03:53:26 +0900,
    Yukihiro Matsumoto wrote in [ruby-talk:316501]:
    > Thank you for your valuable input. And don't worry I have not decided
    > to put this in the language, even after 2.0.


    How about making all local variables other than block arguments
    method/class/toplevel?

    --
    Nobu Nakada
     
    Nobuyoshi Nakada, Oct 1, 2008
    #12
  13. Mike Gold wrote:
    > Yukihiro Matsumoto wrote:
    >> Yes, but
    >>
    >> * Class.new and define_method is a rather rare examples, of which
    >> non-block counterpart introduce new scopes.

    >
    > But it's not a rare example for me. It's a great solution to an
    > otherwise tangled problem. When I have more time I can post some code
    > if my example is not convincing.


    Yes, I would love to see some non-contrived example code. I don't use
    define_method very often and I have hardly ever used Class.new,
    certainly not with several levels of nested blocks. The common case for
    which I *would* like to use this suggested featured is something like this:

    1 collection.each do |element|
    2 changed = true if element.change
    3 end
    4 do_something if changed

    Are you really saying that upon reading line 4 you have to backtrack to
    line 2 in order to understand this code?

    Far from a monster of O(n^2) complexity, this is the kind of code that
    most programmers would expect to work. Except it doesn't, and if I have
    to hazard a guess I would say that 90% of ruby newbies have run into
    that gotcha.

    Of course we all learn to work around it and even exploit it, but it
    doesn't mean it was right to begin with. While "intuitive" and "POLS"
    are loaded terms, if 90% of newbies bump into a gotcha, IMHO it's the
    sign of a design flaw, and matz was right to notice it.

    --
    Daniel
     
    Daniel DeLorme, Oct 1, 2008
    #13
  14. Nobuyoshi Nakada wrote:
    > How about making all local variables other than block arguments
    > method/class/toplevel?


    On the plus side:

    def my_find(a)
    # res=nil not needed!
    a.each { |e| res = e if e =~ /foo/ }
    res
    end

    On the minus side:

    m = lambda { |x,y| tmp = x*2; tmp + y }

    Here tmp becomes a "static" variable (in C-speak) by default, unless you
    write

    m = lambda { |x,y;tmp| ... }

    I think this could cause hard-to-find bugs if the lambda is invoked from
    multiple threads, and a lot of confusion for people expecting functions
    written in this form to be side-effect free.

    I guess it could become another difference between lambda and
    Proc/block. I'm not sure if that would make things less confusing or
    more.

    I still think the current rule ("tmp is part of the closure if it is
    assigned to earlier in the method") is much more sane than the suggested
    alternative ("tmp is part of the closure if it is assigned to earlier
    *or later* in the method")
    --
    Posted via http://www.ruby-forum.com/.
     
    Brian Candler, Oct 1, 2008
    #14
  15. Daniel DeLorme wrote:
    > 1 collection.each do |element|
    > 2 changed = true if element.change
    > 3 end
    > 4 do_something if changed
    >
    > Are you really saying that upon reading line 4 you have to backtrack to
    > line 2 in order to understand this code?


    I think the point is more this:

    class Foo
    attr_reader :changed

    def update
    collection.each do |element|
    element << "\n" if changed # <<<<
    end
    ...
    ...
    ...
    collection.each do |element|
    changed = true if element.change
    end
    end
    end

    At the marked line, you can't tell whether 'changed' is a local variable
    or a method call on this object until you have read forward to the end
    of the method. For me, this is horrid.

    Perhaps more importantly though, as far as I can see it makes irb pretty
    useless, since it can't predict the future. What's supposed to happen
    when you type this, one line at a time?

    def tmp; "hello"; end
    m = lambda { |x| tmp + x }
    m[" world"]
    tmp = "goodbye"
    m[" world"]

    This particular example may be somewhat contrived, but if code behaves
    differently in irb than when read from a source file, that would be
    horrid (and difficult to explain) too.

    Regards,

    Brian.
    --
    Posted via http://www.ruby-forum.com/.
     
    Brian Candler, Oct 1, 2008
    #15
  16. Daniel DeLorme

    Joe Wölfel Guest

    On 1 oct. 08, at 04:18, Brian Candler wrote:

    > Nobuyoshi Nakada wrote:
    >> How about making all local variables other than block arguments
    >> method/class/toplevel?

    >
    > On the plus side:
    >
    > def my_find(a)
    > # res=nil not needed!
    > a.each { |e| res = e if e =~ /foo/ }
    > res
    > end



    res = a.find { |e| e =~ /foo/ }
     
    Joe Wölfel, Oct 1, 2008
    #16
  17. Joe Wölfel wrote:
    > On 1 oct. 08, at 04:18, Brian Candler wrote:
    >
    >> end

    > res = a.find { |e| e =~ /foo/ }


    Yes I know. That's why I called it "myfind". I made a simple concrete
    example of a pattern: iterate doing each, assign something during the
    iteration, use the assigned value outside of the iteration.

    Enumerable#find by itself is not sufficient for all programming needs.
    --
    Posted via http://www.ruby-forum.com/.
     
    Brian Candler, Oct 1, 2008
    #17
  18. Daniel DeLorme

    Joe Wölfel Guest

    Agreed. But it would be a stronger example if it were something
    that couldn't be done easily without a new language feature.


    On 1 oct. 08, at 11:26, Brian Candler wrote:

    > Joe W=F6lfel wrote:
    >> On 1 oct. 08, at 04:18, Brian Candler wrote:
    >>
    >>> end

    >> res =3D a.find { |e| e =3D~ /foo/ }

    >
    > Yes I know. That's why I called it "myfind". I made a simple concrete
    > example of a pattern: iterate doing each, assign something during the
    > iteration, use the assigned value outside of the iteration.
    >
    > Enumerable#find by itself is not sufficient for all programming needs.
    > --=20
    > Posted via http://www.ruby-forum.com/.
    >
     
    Joe Wölfel, Oct 1, 2008
    #18
  19. Daniel DeLorme

    Mike Gold Guest

    Daniel DeLorme wrote:
    > Mike Gold wrote:
    >> Yukihiro Matsumoto wrote:
    >>> Yes, but
    >>>
    >>> * Class.new and define_method is a rather rare examples, of which
    >>> non-block counterpart introduce new scopes.

    >>
    >> But it's not a rare example for me. It's a great solution to an
    >> otherwise tangled problem. When I have more time I can post some code
    >> if my example is not convincing.

    >
    > Yes, I would love to see some non-contrived example code. I don't use
    > define_method very often and I have hardly ever used Class.new,
    > certainly not with several levels of nested blocks. The common case for
    > which I *would* like to use this suggested featured is something like
    > this:


    Because you've hardly ever used Class.new, and therefore are probably
    not familiar with run-time generated classes which present a given
    binding as different views to external onlookers, then you won't
    appreciate what I have to say. To you everything is blub, and you
    wonder why non-blub things are necessary. It's a paradox, that you need
    to understand before you can understand. See
    http://www.paulgraham.com/avg.html

    The strategy I've described comes from Lisp. And it's awesome.

    In fact I think my example does suffice. You just need to imagine it
    being using on a slightly larger scale. As I said previously in this
    thread, "it would be convoluted to create all new scopes each with
    instance variables pointing back to the places I just came from. The
    code would be Java-like: reams of scaffolding which serve no end except
    to compensate for a missing language feature." Do you have a response
    to that? Please don't repeat your argument yet again; instead, try to
    understand what I've said.

    > 1 collection.each do |element|
    > 2 changed = true if element.change
    > 3 end
    > 4 do_something if changed
    >
    > Are you really saying that upon reading line 4 you have to backtrack to
    > line 2 in order to understand this code?


    Yes, I backtracked just now, before I read your last sentence above. I
    checked to be sure that 'changed' means a local variable. I have to do
    this with every variable in arbitrarily long scopes. You are thinking
    in terms of small examples, but I am thinking about large ones.

    > Far from a monster of O(n^2) complexity,


    It is O(n^2) complex because it requires a backtrack for each case. I
    like to be certain what code does. I will always backtrack in order to
    be certain.

    > this is the kind of code that
    > most programmers would expect to work. Except it doesn't, and if I have
    > to hazard a guess I would say that 90% of ruby newbies have run into
    > that gotcha.


    You did not address my counterpoint to your argument here. You just
    repeated your argument.

    --
    Posted via http://www.ruby-forum.com/.
     
    Mike Gold, Oct 1, 2008
    #19
  20. Daniel DeLorme

    Pit Capitain Guest

    2008/10/1 Mike Gold <>:
    > Daniel DeLorme wrote:
    >> Yes, I would love to see some non-contrived example code. I don't use
    >> define_method very often and I have hardly ever used Class.new,
    >> certainly not with several levels of nested blocks. The common case for
    >> which I *would* like to use this suggested featured is something like
    >> this:

    >
    > Because you've hardly ever used Class.new, and therefore are probably
    > not familiar with run-time generated classes which present a given
    > binding as different views to external onlookers, then you won't
    > appreciate what I have to say.


    So Mike, please show us a real non-contrived example to enlighten us.
    I've often used Class.new but still don't see your problem.

    > In fact I think my example does suffice.


    No it doesn't. In your example there's no obvious need to use
    Class.new and define_method.

    >> Far from a monster of O(n^2) complexity,

    >
    > It is O(n^2) complex because it requires a backtrack for each case. I
    > like to be certain what code does. I will always backtrack in order to
    > be certain.


    You have to do that in current Ruby, too, because you have to check
    for local variables that have been assigned to before the code blocks.
    Expanding the checks to the end of the scope doesn't change the O
    complexity.

    Regards,
    Pit
     
    Pit Capitain, Oct 1, 2008
    #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. Paul Opal
    Replies:
    12
    Views:
    1,008
    Paul Opal
    Oct 11, 2004
  2. morrell
    Replies:
    1
    Views:
    1,005
    roy axenov
    Oct 10, 2006
  3. Steve V
    Replies:
    6
    Views:
    260
    Steve V
    Apr 20, 2005
  4. Iñaki Baz Castillo
    Replies:
    14
    Views:
    266
    Iñaki Baz Castillo
    Apr 5, 2009
  5. Iñaki Baz Castillo
    Replies:
    7
    Views:
    705
    Rick DeNatale
    Dec 3, 2009
Loading...

Share This Page