Ruby1.9 block scope

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.

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.

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.

Hi,

In message “Re: ruby1.9 block scope”
on Wed, 1 Oct 2008 02:06:38 +0900, Mike G.
[email protected] writes:

|I hope I’m not belaboring the point, but –

Yes, but

  • Class.new and define_method is a rather rare examples, of which
    non-block counterpart introduce new scopes.

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

           matz.
    

Daniel DeLorme wrote:

Mike G. 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.

Yukihiro M. 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.

Hi,

At Wed, 1 Oct 2008 03:53:26 +0900,
Yukihiro M. 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?

Mike G. wrote:

Yukihiro M. 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.

Hi,

In message “Re: ruby1.9 block scope”
on Wed, 1 Oct 2008 03:40:08 +0900, Mike G.
[email protected] writes:

|> * 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.

Thank you for your valuable input. And don’t worry I have not decided
to put this in the language, even after 2.0.

          matz.

Nobuyoshi N. 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”)

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.

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

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

Hello to all.

I wonder if someone is able to help me out. I’m using the RSCM gem to
help us with our deployment and am running into some difficulties when
using RSCM::Subversion.diff to get the changes made since a “base”
revision.

When i call RSCM::Subversion.diff i get the following error:
/
scm.diff(“branches/optimization/Rakefile”, 1533, 1665)/

/svn: Can’t write to stream: The pipe is being closed.
c:/ruby/lib/ruby/gems/1.8/gems/rscm-0.5.1/lib/rscm/command_line.rb:111:in
verify_exit_code': (RSCM::CommandLine::ExecutionError) dir : C:/Documents and Settings/Jim/My Documents/Work/OffroadDeployR command : svn diff --revision 1533:1665 "svn://svn.******/Ruby/****/branches/optimization/Rakefile" executed command : svn diff --revision 1533:1665 "svn://svn.******/Ruby/****/branches/optimization/Rakefile" exitstatus: 1 STDERR TAIL START doesn't exist STDERR TAIL END from c:/ruby/lib/ruby/gems/1.8/gems/rscm-0.5.1/lib/rscm/command_line.rb:140:ine’
from
c:/ruby/lib/ruby/gems/1.8/gems/rscm-0.5.1/lib/rscm/command_line.rb:70:in
execute' from c:/ruby/lib/ruby/gems/1.8/gems/rscm-0.5.1/lib/rscm/base.rb:281:inexecute’
from
c:/ruby/lib/ruby/gems/1.8/gems/rscm-0.5.1/lib/rscm/scm/subversion.rb:82:in
diff' from svn_wrapper.rb:30 from svn_wrapper.rb:29:ineach’
from svn_wrapper.rb:29
from svn_wrapper.rb:28:in each_pair' from svn_wrapper.rb:28 from c:/ruby/lib/ruby/gems/1.8/gems/rscm-0.5.1/lib/rscm/revisions.rb:64:ineach’
from
c:/ruby/lib/ruby/gems/1.8/gems/rscm-0.5.1/lib/rscm/revisions.rb:64:in
`each’
from svn_wrapper.rb:7

/However if i copy the svn diff cmd from the error codeand run it from a
command line, I get a diff of the files as expected.:

/svn diff --revision 1533:1665
"svn://svn.
/Ruby//branches/optimization/Rakefile"/
/
/I’m running Ruby 1.8.6 on Windows XP with the RSCM v0.5.1 (oh and
Subversion 1.5.1). If i’ve missed anything let me know. Would really
appreciate help if anyone can.

Cheers
Jim

Joe Wölfel wrote:

On 1 oct. 08, at 04:18, Brian C. 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.

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

Daniel DeLorme wrote:

Mike G. wrote:

Yukihiro M. 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.

Brian C. wrote:

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

IMHO, the res=nil is necessary to preserve the semantics of a closure:

A lambda/closure should enclose the variables around it into a neat
package. It should not emit new variables into its surroundings!
Otherwise, it would be something like an “opener” instead of a
“closure”.

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”)

I agree wholeheartedly and I hope Ruby does not change in this regard.

2008/10/1 Mike G. [email protected]:

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

Joe Wölfel wrote:

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

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

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

Actually that has a slightly different effect.
Brian’s my_find finds the last matching expression. :wink:

Pit C. wrote:

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.

Do you understand my previous paragraph:

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.

I ask because I’ve seen no indication that others have gotten it besides
matz. A larger example would be just what I’ve written already, only
larger. But if we aren’t first on the same page with respect to the
above, the larger example will not help either. There is nothing I’m
holding back. If it’s not obvious then ask a question.

I would suggest to actively seek what is meant by the above, rather than
to passively demand code which will infuse you with enlightenment. It
won’t.

Rather, treat it as a homework problem: construct a case where a set of
dynamically generated unique classes bound to the current context
results in better code compared to a set of named classes defined in a
different scopes containing instance variables to that current context.
Go.

I don’t mean to lecture, but after the necessary tools have been laid on
the table, I do resent students who demand to be shown. It is only the
curious who really learn. Questions are welcome. Demands are not.

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.

But you don’t have to check for locals that have been assigned after.
See my example.