Is there some way to execute a block within an arbitrary lexicalscope?

R

Ruby Talk

Is there some way to execute a block within a certain lexical scope?

Let's say the scope I want is inside B:
module A
class B
# .. wanna get in here
end
end

And I wanted something like:

method_returning_B_in_lexical_scope_of_A.class_eval do
...
end

As most of you know 'A::B.class_eval' does not cut it, since A would not
be in the nesting.

The obvious reason I want this is to take advantage of the nesting for
lookups.

Thanks for any ideas!

-- Ara Vartanian
 
R

Robert Klemme

Is there some way to execute a block within a certain lexical scope?

Let's say the scope I want is inside B:
module A
class B
# .. wanna get in here
end
end

And I wanted something like:

method_returning_B_in_lexical_scope_of_A.class_eval do
...
end

As most of you know 'A::B.class_eval' does not cut it, since A would not
be in the nesting.

The obvious reason I want this is to take advantage of the nesting for
lookups.

Why do you need it? There *is* nested lookup already:

robert@fussel ~
$ ruby -e 'module A;X=1;class B;def x;X;end;end;end;p A::B.new.x'
1

Cheers

robert
 
R

Ruby Talk

Let's say I'm doing some metaprogramming from another class and I want
to do something in B's full lexical class scope.
 
R

Robert Klemme

2008/5/2 ara.t.howard said:
do something in B's full lexical class scope.

What exactly is this "something"?
then you will need to use continuations.

It depends of course what should be achieved. A simple constant
lookup is easily done via #const_get. Other things can be achieved
via #instance_eval.

Cheers

robert
 
R

Ruby Talk

Robert,

Well, let's say that that something is a new class I want to define
within that scope. And I'd like the innards of that class to be able to
take advantage of the the natural, idiomatic lookup rules, where the
interpreter traipses up the ancestor chain.

To be more clear, let's say I have

X = :bad
module A
X = :good
class B
#...let us say I want to execute something here where X == :good
end
end

And I'd like to execute something from outside that lexical scope (let's
say from my metaprogramming classes) within that [A, A::B] lexical scope.

I guess what I'm asking is: is there any way of faking lexical scope
(for the purposes of metaprogramming)?
 
R

Ruby Talk

Ara,

Thanks for your suggestion. I'm a little hazy on how this would help. If
I defined a proc or code block or whatever elsewhere, it's bound to the
lexical scope where it was defined. How does calling it from the
continuation help given that?

Anyway, maybe that's not what you were thinking, and it's likely I'm
missing the obvious here. I'm not the best at problem solving with
continuations.

-- Ara Vartanian
 
S

Sean O'Halpin

Is there some way to execute a block within a certain lexical scope?

Let's say the scope I want is inside B:
module A
class B
# .. wanna get in here
end
end

And I wanted something like:

method_returning_B_in_lexical_scope_of_A.class_eval do
...
end

As most of you know 'A::B.class_eval' does not cut it, since A would not be
in the nesting.

The obvious reason I want this is to take advantage of the nesting for
lookups.

Thanks for any ideas!

-- Ara Vartanian
Hi,

there doesn't appear to be any form of block invocation that changes
how constants are looked up.

Some experiments:

X = :bad
module A
X = :good
class B
#...let us say I want to execute something here where X == :good
end
end
# as you state, this doesn't work
A.module_eval { X } # => :bad
# nor this
A.instance_eval { X } # => :bad
# nor this
A::B.new.instance_eval { X } # => :bad
# this does of course
module A
X # => :good
end
# and this
module A
class B
X # => :good
end
end
# ...but this surprised me
class A::B
X # => :bad
end
# this doesn't work either
context_A = A.module_eval { binding }
eval("X", context_A) # => :bad
# you have to use a string
context_A = A.module_eval "binding"
eval("X", context_A) # => :good
# but again
context_B = A::B.module_eval "binding"
eval("X", context_B) # => :bad
# so you can't parameterize this which means you have to do something
# like:
def eval_in_namespace(namespace, str)
constants = []
namespace.split(/::/).reject{|x|
x.empty?}.inject(Module.const_get(self.class.to_s)) { |prev, this|
(constants << prev.const_get(this)).last
}
prefix = constants.map{ |x| "#{x.class.to_s.downcase} #{x}"}.join(';')
suffix = constants.map{ 'end'}.join(';')
eval "#{prefix}; #{str}; #{suffix}"
end
eval_in_namespace("A::B", "X") # => :good
# not very pretty :S
# of course, you could just use:
A::X # => :good
# but not
A::B::X # => :bad # !> toplevel constant X referenced by A::B::X


Ara - could you shed some light on how continuations would help here?
I don't see it myself.

Regards,
Sean
 
R

Robert Klemme

Please do not top post.

Well, let's say that that something is a new class I want to define
within that scope. And I'd like the innards of that class to be able to
take advantage of the the natural, idiomatic lookup rules, where the
interpreter traipses up the ancestor chain.

To be more clear, let's say I have

X = :bad
module A
X = :good
class B
#...let us say I want to execute something here where X == :good
end
end

And I'd like to execute something from outside that lexical scope (let's
say from my metaprogramming classes) within that [A, A::B] lexical scope.

I guess what I'm asking is: is there any way of faking lexical scope
(for the purposes of metaprogramming)?

For constant lookup there is:

robert@fussel /cygdrive/c/Temp
$ ruby lex.rb
2

robert@fussel /cygdrive/c/Temp
$ ruby lex.rb
2
2

robert@fussel /cygdrive/c/Temp
$ cat lex.rb

class Module
def lex_lookup(c)
name.split('::').inject([TOPLEVEL_BINDING,[]]) do |(b,o),n|
b = (b.const_get n rescue eval(n,b))
[b, o << b]
end.last.reverse.each do |cl|
return cl.const_get(c) if cl.const_defined? c
end
raise NameError, c
end
end

X=1
class Foo
X=2
class Bar
puts lex_lookup("X")
end
puts lex_lookup("X")
end

robert@fussel /cygdrive/c/Temp
$

You can easily extend that to include top level constants as well as
other evaluations.

Cheers

robert
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,205
Messages
2,571,067
Members
47,673
Latest member
MahaliaPal

Latest Threads

Top