Newbie Question. eval.rb variable scope issue

I am a Python duffer who got interested in Rails an decided to check out
Ruby.

If there is a preferred way to seek help on this please, let me know.

Downloaded & installed the Windows version.

ruby -v answers ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32]

I found eval.rb in the sample library and got some unexpected results,
using examples straight from one of the tutorials.

Here is the most simplified version I could generate:

ruby> word = 2

2

ruby> word

ERR: undefined local variable or method `word’ for main:Object

ruby>

This seems to be related to the scope of local variables, which is
different for each iteration of the main loop in eval.rb.

This dandy little Ruby script illustrates the issue

a = [‘word = 2’, ‘word’]

a.each do | line |

   begin

          print eval(line).inspect, "\n"

   rescue ScriptError, StandardError

          print line + "\n"   # WJR change

          printf "ERR: %s\n", $! || 'exception raised'

   end

end

print “end of interation\n”

begin

   print eval('word = 1').inspect, "\n"

rescue ScriptError, StandardError

   print line + "\n"   # WJR change

   printf "ERR: %s\n", $! || 'exception raised'

end

print “x\n”

begin

   print eval('word').inspect, "\n"

rescue ScriptError, StandardError

   print line + "\n"   # WJR change

   printf "ERR: %s\n", $! || 'exception raised'

end

Running it in SciTE gives the following results:

ruby multiEvalCallsA.rb

2

word

ERR: undefined local variable or method `word’ for main:Object

end of interation

1

x

1

Exit code: 0

Of course if you switch the order, the program runs without an error
message, since the non-iterated version establishes the variable ‘word’
at the outer scope.

After some perusing the available on-line manuals I have not been able
to find a way to fix eval.rb to remember the scope of variables created
in the input to it. Perhaps I am missing something obvious?

Wes Rishel
Research Vice President
Gartner
Alameda, CA
+1 510 217 3085

Gartner clients: for inquiries contact
[email protected]
mailto:[email protected]
or phone: +1 203 316 1122

see us at

Gartner’s Healthcare Summit 2007
The Doral Golf Resort, Miami, Florida, November 11-14, 2007
www.healthcareitsummit.com http://www.healthcareitsummit.com/

This e-mail message, including any attachments, is for the sole use of
the person to whom it has been sent, and may contain information that is
confidential or legally protected. If you are not the intended recipient
or have received this message in error, you are not authorized to copy,
distribute, or otherwise use this message or its attachments. Please
notify the sender immediately by return e-mail and permanently delete
this message and any attachments. Gartner makes no warranty that this
e-mail is error or virus free.

From: “Rishel,Wes” [email protected]

          print eval(line).inspect, "\n"
   rescue ScriptError, StandardError
          print line + "\n"   # WJR change
          printf "ERR: %s\n", $! || 'exception raised'
   end

end

After some perusing the available on-line manuals I have not been able
to find a way to fix eval.rb to remember the scope of variables created
in the input to it. Perhaps I am missing something obvious?

Welcome !

I don’t know the exact reason, but it seems you’ll need to
obtain a binding context from outside the iterator block.
E.g.

a = [‘word = 2’, ‘word’]
b = binding
a.each {|line| p( eval(line, b) ) }

Incidentally, have you found irb yet? There should be an irb.bat
in the bin directory next to your ruby.exe. IRB is “interactive
ruby”, similar to invoking python with no arguments.

A couple tips:

p(expr) is a shortcut for puts(expr.inspect)

So,

print eval(line).inspect, “\n”

can be written as:

p line

Also, puts is like print, but adds the “\n” for you.
So,

print line + “\n”

can be:

puts line

Hope this helps,

Regards,

Bill

If you installed cygwin (+bash) just alias irb to the /ruby/irb.bat or
wherever it resides. In irb you can really dump-test ruby code quickly.

“who got interested in Rails an decided to check out
Ruby.”

I think you should give ruby a good chance, so to understand it.
Often people that use rails end up learning ruby (at least understanding
the syntax things), and IMHO its better to know how the core concepts of
ruby work, how to write your own classes, modules methods etc…

Btw instead of
print line + “\n” # WJR change
you could just do
puts line

Thanks, Bill. It sounds like irb will fill my needs.

I have to say that featuring eval in the tutorials when it doesn’t work
for a simple set of interactions like setting a variable and then
printing it seems very newbie unfriendly.

Some languages have a culture that excludes all but the smartest and
those who are willing to devote all their time to memorizing the
nuances. I would put Perl in that category. The initial “affect” of the
Ruby language and community is else-wise, but a couple of things make me
wonder. This includes tutorials that don’t work in every case and the
fact that there is no free version of documentation for the current
version, which was released > 3 months ago.

If it weren’t for people like you willing to respond it could be very
off-putting indeed.

On 7/1/07, Rishel,Wes [email protected] wrote:

I am a Python duffer who got interested in Rails an decided to check out
Ruby.

Welcome to the jewel.
Local variables spring into existance in the scope they are assigned to
Our friend irb shows this quite nicely

irb(main):008:0> %w{ word=2 word }.each{|c| p eval( c )}
2
NameError: undefined local variable or method word' for main:Object from (irb):8 from (irb):8:in eval’
from (irb):8
from (irb):8:in `each’
from (irb):8
from :0
irb(main):009:0> word=nil
=> nil
irb(main):010:0> %w{ word=2 word }.each{|c| p eval( c )}
2
2
=> [“word=2”, “word”]

eval creates a scope of it’s own, thus eval(“word=2”) brings it into
life and it sadly dies.
But when eval(“word=2”) assigns to the already existing local variable
in the outer scope it persists into the next eval.

And just a friendly hint, forget eval you do not need it :).

HTH
Robert

Thanks, Marc. I will try that.

BTW, I agree with your other advice. I won’t start to try Rails until I
feel reasonably competent at writing OO code in Ruby.

On 7/1/07, Rishel,Wes [email protected] wrote:

wonder. This includes tutorials that don’t work in every case and the
fact that there is no free version of documentation for the current
version, which was released > 3 months ago.

If it weren’t for people like you willing to respond it could be very
off-putting indeed.

I would like to know which tutorial out there you were following that
teaches use of eval(). The Ruby Way generally does not recommend the
use of eval with a string unless you are doing some very advanced
work, and even then it is very risky business.

Ruby does have it’s nuances, but this community, as you can see, is
quite willing to help you out when you have found one, and help you
figure out what it is and why it is. I don’t know which tutorial you
are referring to, because none of the “official” tutorials on the
ruby-lang.org website mention eval. If you want a good place to start
learning ruby, I suggest you check out _why’s poignant guide to ruby
(http://poignantguide.net/ruby/), it is a little wacky, but it teaches
the language and the concepts of the ruby way very well.

As to no free documentation for the most recent version of ruby, one
ships with your ruby install (the ri tool and the rdocs) and there is
ruby-doc.org.

Welcome to Ruby, if you hit any other major problems feel free to ask.
We also have a lively IRC channel #ruby-lang on freenode. Hope you
like the language. And be aware, the philosophy guiding Ruby is not
the same as that of python’s.

Thanks Chris, for taking the time to further this discussion. As you
point out the community does seem to be very responsive.

I regret that I got confused and did not recognize that the standard
documentation is up-to-date. I got to several documents through the
documentation sub-menu for Ruby Getting Started Link that were described
as out of date for free and then spoke too hastily.

To your main comment:

The method eval is not what is featured in the tutorial, it is eval.rb,
a script in the Ruby Samples library. My comments were directed to the
use of the script in the tutorial.

Through the Getting Started link that came with the Ruby install
package, I ended up in a tutorial on this page:

http://www.ruby-doc.org/docs/UsersGuide/rg/examples.html

It is the page that recommends the use of eval.rb and, in fact, uses the
script in a sequence of examples that extends across several web pages.

On the next page,
http://www.ruby-doc.org/docs/UsersGuide/rg/strings.html, there is an
example:

ruby> var = " abc "
" abc "
ruby> “1234#{var}5678”
“1234 abc 5678”

On my machine eval.rb gives a different result.

ruby> var = " abc "
" abc "
ruby> “1234#{var}5678”
ERR: undefined local variable or method `var’ for
main:Object
ruby>

Looking at the scoping rules for Ruby it is hard to understand how
eval.rb was ever able to perform the examples in the tutorial, since the
current output is entirely consistent with the scoping rule. It is also
hard to perceive how one could write a version of eval.rb that would
work, given that it pretty much has to have a loop.

While I feel it would be premature for me to opine on the Ruby
semantics, I have to say I am having difficulty perceiving the value of
making context bindings on substructures of code within a method when
there is no explicit declaration of the context block other than the
loop-defining statement.

It is hard to say which of this various principles this supports.
Arguably, it is not the principle of least surprise, since deleting a
statement in the start of a method can alter the method’s behavior many
lines down in a loop. The rationales I can think of would be about
helping the computer run more efficiently. It seems that the
philosophies around Ruby are oriented to helping the programmer rather
than helping the machine. So I guess I am missing the manner in which
this helps.

I am not holding Python up for comparison, just trying to understand
Ruby on its own terms.

Robert,

I have said repeatedly that I am not (a) expressing direct opinions on
Ruby – the language, or (b) attempting to compare it to Python.

In another posting I provided the URL to what appears to be the prime
tutorial on Ruby, at least it is featured in a set of links delivered
with the software. Simple examples don’t work.

If I dared to comment that this might be off-putting to the newbie then
I guess whether that is hostile or not depends a lot on the culture of
the community. I have certainly been careful to avoid being accusatory,
sarcastic and to keep the “tone” of my emails emotionally neutral. To me
this seems like the courteous thing to do.

So far I have given examples that don’t work in the tutorial that is
recommended in the software distribution, and asked questions to help me
understand why the Ruby approach to local variable scoping is meant to
work the way it does.

Apparently you feel that this is hostile. All I can hope is that others
in the community are more open to discussion that is collegial.

If most people on the list feel I am not being collegial then I will try
to figure out why. Usually I have good luck at keeping the discussion
focused on issues.

Alle domenica 1 luglio 2007, Rishel,Wes ha scritto:

            ERR: undefined local variable or method `var' for

main:Object
ruby>

I don’t know why you’re getting this behaviour. On my system (linux,
ruby
1.8.6 p36), the example works correctly:

ruby> var = " abc "
" abc "
ruby> “1234#{var}5678”
“1234 abc 5678”

Looking at the scoping rules for Ruby it is hard to understand how
eval.rb was ever able to perform the examples in the tutorial, since the
current output is entirely consistent with the scoping rule. It is also
hard to perceive how one could write a version of eval.rb that would
work, given that it pretty much has to have a loop.

Here, I think you’re wrong. The Pickaxe first edition states it clearly
in
the “Expressions” chapter (section “Variable scope and loops”):
“The while, until, and for loops are built into the language and do not
introduce new scope; previously existing locals can be used in the loop,
and
any new locals created will be available afterward.”
This is different from what happens with blocks, where variables created
inside the block aren’t accessible outside them. Since eval.rb uses a
while
loop, instead, variables defined in one iteration are kept in the
following.

Stefano

Alle domenica 1 luglio 2007, Stefano C. ha scritto:

I don’t know why you’re getting this behaviour

Now I know. I was using the eval.rb script provided on the tutorial
website,
while you are using the one provided with the ruby distribution.

Stefano

On 7/1/07, Rishel,Wes [email protected] wrote:

Thanks, Bill. It sounds like irb will fill my needs.

I have to say that featuring eval in the tutorials when it doesn’t work
for a simple set of interactions like setting a variable and then
printing it seems very newbie unfriendly.
Well as I showed above it works if this is unclear from the tutorial
than this is indeed bad a tutorial, maybe you would care to provide us
with a link?
Maybe you rather want to have a look at this
Programming Ruby: The Pragmatic Programmer's Guide

Some languages have a culture that excludes all but the smartest and
those who are willing to devote all their time to memorizing the
nuances. I would put Perl in that category. The initial “affect” of the
Ruby language and community is else-wise, but a couple of things make me
wonder. This includes tutorials that don’t work in every case and the
fact that there is no free version of documentation for the current
version, which was released > 3 months ago.
But I am not sure I can follow your vision, we are quite aware that
the documentation of Ruby is not top - but there is great stuff out
there, like mentioned above.
That said I do not think eval does not work, at least the behavior you
have described is completely normal.
Eval is not the point where to start and if someone starts at the
toughest spot she will realize err that she started at the toughest
spot.

It might not be the best approach to learn Ruby by criticizing as you
do, not that someone will care too much, you are just losing your
time.

If it weren’t for people like you willing to respond it could be very
off-putting indeed.

This is maybe the second time I have heard this complaint in this
list; after two years of reading it, but we are all different and if
you feel so, sorry.

Cheers
Robert

Alle domenica 1 luglio 2007, Rishel,Wes ha scritto:

It is reassuring to know that the problem is a local problem.

It’s not a local problem. As I said in my second message, there are two
files
called eval.rb involved in this problem. One is included in the ruby
distribution, is the one you’re using and doesn’t allow to run the code
in
the tutorial; the other is the one in the tutorial website
(http://www.ruby-doc.org/docs/UsersGuide/rg/eval.txt), which is the one
I
used, which works correctly. The latter, as the tutorial itself says, is
more
advanced, but the main difference is that it uses ‘while’, while the
other
uses ‘loop’. Since ‘loop’ introduces a new scope, variables defined in
one
iteration are lost in the next - so the bug. ‘while’ doesn’t introduce a
new
scope, so the program works.

Generally, the scope of a local variable is one of

    proc{ ... }
    *** loop{ ... } *** (my emphasis)
    def ... end
    class ... end
    module ... end
    the entire program (unless one of the above applies)

You’re mostly right here. proc and loop are instance methods of the
Kernel
module, so they don’t create a new scope. What happens is that you pass
these
methods a block, and it is the block which introduces a new scope (the
same
happens for every method which accepts a block).

I hope this helps

Stefano

It is reassuring to know that the problem is a local problem. I hope
someone will be able to help me understand why it might be happing.
Perhaps someone on Windows could try the example and see if it’s an
issue with the distribution package?

In believing that the example failing was consistent with what was
written in the same tutorial later on. It states

The first assignment you make to a local variable acts something like a
declaration. If you refer to an uninitialized local variable, the ruby
interpreter thinks of it as an attempt to invoke a method of that name;
hence the error message you see above.

Generally, the scope of a local variable is one of

proc{ … }
*** loop{ … } *** (my emphasis)
def … end
class … end
module … end
the entire program (unless one of the above applies)

There are further examples in the tutorial that support my
interpretation:

ruby> foo = 44; print foo, “\n”; defined? foo
44
“local-variable”
ruby> loop{bar=45; print bar, “\n”; break}; defined? bar
45
nil

I must be missing something.

Stefano, mil grazie!

I don’t know how to pass a suggestion to those who maintain the library,
but it would be a one-line change to make the “simple” one use while 1
instead of loop do.

On 7/1/07, Rishel,Wes [email protected] wrote:
Please do not top post.

I guess whether that is hostile or not depends a lot on the culture of
the community. I have certainly been careful to avoid being accusatory,
sarcastic and to keep the “tone” of my emails emotionally neutral. To me
this seems like the courteous thing to do.

So far I have given examples that don’t work in the tutorial that is
recommended in the software distribution, and asked questions to help me
understand why the Ruby approach to local variable scoping is meant to
work the way it does.

Apparently you feel that this is hostile.
No I do not at all.
All I can hope is that others
in the community are more open to discussion that is collegial.

If most people on the list feel I am not being collegial then I will try
to figure out why. Usually I have good luck at keeping the discussion
focused on issues.
What issue? Eval, local scopes or an error in a Tutorial you do not link
to?
Funny I just got the feeling that you are worried about writing
speeches, well if I could not help than I could not help, no problem
at all.

Robert