[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

eval/binding question

Stefan Kaes

3/15/2005 2:03:00 PM

I tried to create local variables from a name=>value hash passed as a
parameter, but I can't get it to work. The following test program shows
what is happening:

def test1
eval "x=25"
eval 'print "x=#{x}\n"'
end

test1

def test2
eval "x=25"
print "x=#{x}\n"
end

test2

produces:

x=25
test.rb:189:in `test2': undefined local variable or method `x' for
main:Object ( NameError)
from test.rb:192

It seems that changes to local variables are not reflected back to the
executing environment.

Does anyone out there know how to solve this problem?

-- stefan

22 Answers

Robert Klemme

3/15/2005 3:15:00 PM

0


"Stefan Kaes" <skaes@gmx.net> schrieb im Newsbeitrag
news:4236E882.3050602@gmx.net...
> I tried to create local variables from a name=>value hash passed as a
> parameter, but I can't get it to work. The following test program shows
> what is happening:
>
> def test1
> eval "x=25"
> eval 'print "x=#{x}\n"'
> end
>
> test1
>
> def test2
> eval "x=25"
> print "x=#{x}\n"
> end
>
> test2
>
> produces:
>
> x=25
> test.rb:189:in `test2': undefined local variable or method `x' for
> main:Object ( NameError)
> from test.rb:192
>
> It seems that changes to local variables are not reflected back to the
> executing environment.
>
> Does anyone out there know how to solve this problem?

You can't. Local variables are determined during parse phase and you
cannot access local variables that you create via eval other than via
eval. Consider

16:11:59 [robert.klemme]: ruby -e 'eval "x=10";puts x'
-e:1: undefined local variable or method `x' for main:Object (NameError)
16:13:51 [robert.klemme]: ruby -e 'x=nil;eval "x=10";puts x'
10
16:13:57 [robert.klemme]: ruby -e 'eval "x=10";eval "puts x"'
10

IOW, you can manipulate predefined locals but not those that spring into
existence during an eval.

Kind regards

robert

Yukihiro Matsumoto

3/15/2005 3:33:00 PM

0

Hi,

In message "Re: eval/binding question"
on Tue, 15 Mar 2005 23:02:44 +0900, Stefan Kaes <skaes@gmx.net> writes:

|I tried to create local variables from a name=>value hash passed as a
|parameter, but I can't get it to work. The following test program shows
|what is happening:

local variables should be determined at compile time, thus local
variables defined first in the eval'ed string, can only be accessed from
other eval'ed strings. In addition, they will be more ephemeral in
Ruby2, so that these variables will not be accessed from outside.

In summary, I recommend you not to use local variables for your
purpose. They are wrong tool for it.

matz.


Stefan Kaes

3/15/2005 4:18:00 PM

0

Yukihiro Matsumoto wrote:

>Hi,
>
>In message "Re: eval/binding question"
> on Tue, 15 Mar 2005 23:02:44 +0900, Stefan Kaes <skaes@gmx.net> writes:
>
>|I tried to create local variables from a name=>value hash passed as a
>|parameter, but I can't get it to work. The following test program shows
>|what is happening:
>
>local variables should be determined at compile time, thus local
>variables defined first in the eval'ed string, can only be accessed from
>other eval'ed strings. In addition, they will be more ephemeral in
>Ruby2, so that these variables will not be accessed from outside.
>
>
Well, I don't agree. From a language designers point of view x=5 and
eval "x=5" should do the same thing: modify the current binding by
introducing a new value-binding with name x and value 5. I don't know of
any language which behaves differently (e.g. LISP works like this
AFAIK). Of course, as a compiler writer, one might prefer to be able to
determine all local variables by looking at the source. But this is just
a wish to make the compiler simpler or enable better optimization.

>In summary, I recommend you not to use local variables for your
>purpose. They are wrong tool for it.
>
> matz.
>
Maybe I should explain why I tried this:

when using ERB one can compile a template into source, which can then be
eval'ed using eval. Local variables for use in the template can be set
up using the sort of eval given in my example. However, in this case the
'compiled' template code gets reparsed on each evaluation of the
template code. I wanted to speed up this process by defining a function
(eval "def fun; #{src}; end", aBinding), thereby pasring the code only
once. This works pretty well and gives about 25% increase in speed. The
performance gain will be much bigger, once ruby gets a real JIT. But, as
it turned out, local variables are not accessible when the defined
function is executed.

I saw two solutions: First, change the local variables to instance
variables on the class executing the function. This works, but does not
feel right (name space pollution). Second, put all locals in an instance
hash and set up local functions inside the defined function from the
hash. Which does not work.

Any other ideas?


Yukihiro Matsumoto

3/15/2005 4:33:00 PM

0

Hi,

In message "Re: eval/binding question"
on Wed, 16 Mar 2005 01:17:55 +0900, Stefan Kaes <skaes@gmx.net> writes:

|>local variables should be determined at compile time, thus local
|>variables defined first in the eval'ed string, can only be accessed from
|>other eval'ed strings. In addition, they will be more ephemeral in
|>Ruby2, so that these variables will not be accessed from outside.
|
|Well, I don't agree. From a language designers point of view x=5 and
|eval "x=5" should do the same thing: modify the current binding by
|introducing a new value-binding with name x and value 5. I don't know of
|any language which behaves differently (e.g. LISP works like this
|AFAIK). Of course, as a compiler writer, one might prefer to be able to
|determine all local variables by looking at the source. But this is just
|a wish to make the compiler simpler or enable better optimization.

You don't have to agree here. Each language designer have different
point of view. I'm not going to change my mind by your "should".

|when using ERB one can compile a template into source, which can then be
|eval'ed using eval. Local variables for use in the template can be set
|up using the sort of eval given in my example. However, in this case the
|'compiled' template code gets reparsed on each evaluation of the
|template code. I wanted to speed up this process by defining a function
|(eval "def fun; #{src}; end", aBinding), thereby pasring the code only
|once. This works pretty well and gives about 25% increase in speed. The
|performance gain will be much bigger, once ruby gets a real JIT. But, as
|it turned out, local variables are not accessible when the defined
|function is executed.

I'm not sure I'm fully understand you. Can you show us your code?

matz.


Stefan Kaes

3/15/2005 4:51:00 PM

0

Yukihiro Matsumoto wrote:

>I'm not sure I'm fully understand you. Can you show us your code?
>
>
Yes, but I need to clean up first.

-- stefan


George Moschovitis

3/15/2005 5:15:00 PM

0

> Maybe I should explain why I tried this:
>
> when using ERB one can compile a template into source, which can then

> be eval'ed using eval. Local variables for use in the template can be
set
> hash and set up local functions inside the defined function from the

Well, that's funny :)

I just finished working on a VERY similar bit of code.

FYI, I came up with a sligthly altered version of your first solution.
In my case, name space polution is not a problem.

The code will be available soon as part of Nitro's new Mailer
subsystem.

regards,
-g.

ps:


--
http://nitro.rub...

Stefan Kaes

3/15/2005 5:46:00 PM

0

code attached.

In the real environment, the names of the locally assigned variables can
not be determined in advance, of course.



require 'erb'

class ERBrunner #all compiled template functions go here
def get_binding
binding
end
end

Erb_runner = ERBrunner.new

class Test

def initialize
@erb = ERB.new(File.read("test.rhtml"), nil, '%')
evalstr = "def run_erb\n" +
"bnd = binding\n" +
'@locals.each{|k,v| eval "#{k} = @locals[\"#{k}\"]", bnd }' +
"\n#{@erb.src}\n_erbout\nend\n"
print evalstr, "\n\n"
eval evalstr, Erb_runner.get_binding
end

def runit
Erb_runner.instance_variable_set "@mumu", "HALLO"
Erb_runner.instance_variable_set "@locals", {'mumu'=>"HALLO"}
bnd = Erb_runner.get_binding
eval "Erb_runner.run_erb", bnd
end

end

t = Test.new
print t.runit

locals: <%= @locals.inspect %>
locals['mumu']: <%= @locals['mumu'] %>
mumu: <%= mumu rescue "not defined" %>
@mumu: <%= @mumu %>

Stefan Kaes

3/15/2005 6:30:00 PM

0

Yukihiro Matsumoto wrote:

>You don't have to agree here. Each language designer have different
>point of view. I'm not going to change my mind by your "should".
>
Let me rephrase: the semantics of eval should be defined in a way that
holds no surprises. I would expect that writing

...
eval "code"
...

and

...
code
...

should be identical.

-- stefan




Yukihiro Matsumoto

3/15/2005 11:59:00 PM

0

Hi,

In message "Re: eval/binding question"
on Wed, 16 Mar 2005 03:29:53 +0900, Stefan Kaes <skaes@gmx.net> writes:

|Let me rephrase: the semantics of eval should be defined in a way that
|holds no surprises.

I know what you mean. But it's not good strategy to persuade me to
use "your expectation" or "your surprise". This "limitation" has a
lot of good aspects, such as better performance, better error
detection, etc. I'd love to pay the cost of small restriction for
these benefits as a language designer.

OK, I think this is enough. Let us focus on solving "your problem".

matz.


Yukihiro Matsumoto

3/16/2005 1:46:00 AM

0

Hi,

In message "Re: eval/binding question"
on Wed, 16 Mar 2005 02:45:41 +0900, Stefan Kaes <skaes@gmx.net> writes:

|In the real environment, the names of the locally assigned variables can
|not be determined in advance, of course.

I'm sorry to say you can't pre-compile eRB allowing local variable set
not determined in advance. I know allowing this will make your
program 25% faster, but this also would make any other programs
slower.

If you have limited set of local variables, you can do something like:

require 'erb'

class Test
def initialize(path, *lv)
@erb = ERB.new(File.read(path), nil, '%')
@locals = {}
evalstr = "def run_erb\n" +
lv.collect{|k| "#{k} = @locals[\"#{k}\"]\n"}.join +
"#{@erb.src}\n_erbout\nend\n"
print evalstr, "\n\n"
eval evalstr, nil, path, 0
end
def values(hash)
@locals.update(hash)
hash.each {|k,v|
self.instance_variable_set("@#{k}", v)
}
end
end

t = Test.new("/tmp/test.rhtml", 'mumu')
t.values("mumu" => "HELLO")
print t.run_erb

matz.