Method access to the enclosing method's locals?

Hi,

How can a method gain access to the enclosing method’s locals?

def go2(arg)
def hi
puts “Hi #{arg}!”
end

a={:h => :hi}
send(a[:h])
end

go2 ‘Larry’ ==>> NameError: undefined local variable or method `arg’ for
main:Object

In go2, is there a clean way for the hi method to have access to the
arg local?

Thanks!

Larry

Le vendredi 17 août 2007 17:35, Larry K. a écrit :

send(a[:h])
Larry
Hi,

There is no such thing as a method inside a method. When you wirte ‘def
hi’,
you actually define the method ‘hi’ at the same level than the method
‘go2’.
So, the later cannot acces the data of the former. However, as they are
defined at the same level, they can share data from the instance
variables of
the class they are defined in.

In any case, there is no point to do this.

On 17.08.2007 17:35, Larry K. wrote:

send(a[:h])
end

go2 ‘Larry’ ==>> NameError: undefined local variable or method `arg’ for
main:Object

In go2, is there a clean way for the hi method to have access to the
arg local?

What exactly do you need that for? I am curious because I haven’t
stumbled across a problem where I needed this for.

Kind regards

robert

On 8/17/07, Larry K. [email protected] wrote:

a={:h => :hi}
send(a[:h])
end

go2 ‘Larry’ ==>> NameError: undefined local variable or method `arg’ for
main:Object

In go2, is there a clean way for the hi method to have access to the
arg local?

Hi,

you may try something using 1. blocks, 2. passing actual binding, 3.
there was something called binding of caller, but I’m not sure whether
it still works, as it was based on a bug in ruby implementation.

Maybe if you post a bit higher perspective on your problem somebody
would come up with a solution…

J.

Hi Everybody,

Thank you for your help. To keep things dry, my main method uses one of
a number of methods, depending on the value of an argument. A hash is
used as a selector to choose the right child method.

The child methods need some data, I wanted to see how they could gain
access to the locals of the calling method, ie, the main method.

I appreciate the posts; I’ll continue to pass the arguments, which is
working fine.

Btw, I find it a bit lacking that the syntax checker allows me to
declare a method inside another method since such things don’t exist.
(Per Olivier’s helpful post.)

Regards,

Larry

Hi –

On Sat, 18 Aug 2007, Larry K. wrote:

a={:h => :hi}
send(a[:h])
end

go2 ‘Larry’ ==>> NameError: undefined local variable or method `arg’ for
main:Object

In go2, is there a clean way for the hi method to have access to the
arg local?

def always starts a new local scope. If you want to define a method
but in the same scope, you need to use define_method, probably in
conjunction with class_eval or module_eval on the class or module you
want to put it in.

David

On 8/17/07, David A. Black [email protected] wrote:

The child methods need some data, I wanted to see how they could gain
definition isn’t scoped to the outer definition. This is relatively
new (1.8.6, if I remember correctly), and made it easier to do
something that was always possible anyway with class_eval +
define_method.

ISTR def foo; def bar; end; end working in 1.8.4 and possibly even
1.8.2. (I didn’t hop aboard the ruby train before 1.8.2 though). I
know I’ve used def foo; def bar before 1.8.6.

Hi –

On Sat, 18 Aug 2007, Larry K. wrote:

working fine.

Btw, I find it a bit lacking that the syntax checker allows me to
declare a method inside another method since such things don’t exist.
(Per Olivier’s helpful post.)

I wouldn’t say it doesn’t exist; it’s just that the lexically inner
definition isn’t scoped to the outer definition. This is relatively
new (1.8.6, if I remember correctly), and made it easier to do
something that was always possible anyway with class_eval +
define_method.

David

On 8/17/07, Larry K. [email protected] wrote:

a={:h => :hi}
send(a[:h])
end

def initialize
@submeths = {}
end

def dispatch(name, *args)
if @submeths.has_key? name
@submeths[name].call(*args)
else
send(name, *args)
end
end

def submeth(name, &block)
@submeths[name] = block
end

def go2(arg)

hi = lambda { puts “Hi #{arg}!” }

personally I would skip the submeths ivar altogether and just do

hi.call

but this is mildly more entertaining

submeth(:hi) { puts “Hi #{arg}” }

a={:h => :hi}
dispatch(a[:h])
end

The general concept involved is that of a closure and it is certainly
possible to define closures in ruby and at some point or another someone
pointed out a way of turning closures into methods I don’t remember how
though.

On Behalf Of Larry K.:

Btw, I find it a bit lacking that the syntax checker allows me to

declare a method inside another method since such things don’t exist.

this just a stupid example,

irb(main):040:0> def m1
irb(main):041:1> def m2
irb(main):042:2> “x”
irb(main):043:2> end
irb(main):044:1> end
=> nil
irb(main):045:0> m1
=> nil
irb(main):046:0> m2
=> “x”
irb(main):047:0> m1::m2
=> “x”
irb(main):048:0> def m2
irb(main):049:1> “y”
irb(main):050:1> end
=> nil
irb(main):051:0> m2
=> “y”
irb(main):052:0> m1::m2
=> “x”
irb(main):053:0> m1.m2
=> “x”

kind regards -botp

Here’s one way. First define the closure then use define_method to turn
the
closure into an actual method.

def go2(arg)
closure = proc { puts “Hi #{arg}” }
define_method(:hi,closure)
end

I haven’t tested the above code so I’m not sure if it really works.

Ok, just ignore all my previous posts. I should have done some more
research
before I started typing. This code will actually work.

def go2(arg)
(class << self; self; end).class_eval do
define_method(:hi) do puts “Hi #{arg}” end
end
end

Once you call go2 with some argument it will create the method hi that
will
have access to any argument passed to go2. I have to say though all the
redirection with the singleton class makes my head spin a little.

Sorry, the code below doesn’t work. There are problems with variable
scoping.

Hi –

On Sat, 18 Aug 2007, david karapetyan wrote:

have access to any argument passed to go2. I have to say though all the
redirection with the singleton class makes my head spin a little.

This isn’t quite the same as doing an inner def, because you’re
defining #hi as a singleton method. If you want to define it as an
instance method of the enclosing class, you would want to do:

self.class.class_eval do …

As for the head spinning, hopefully someday we’ll have this method in
the core (and meanwhile you can write it yourself):

class Object
def singleton_class
class << self; self; end
end
end

Then you can do:

singleton_class.class_eval do …

which is a little less cluttered. Also, keep in mind that it’s not
really redirection; it’s just sending messages to objects :slight_smile: The fact
that you can send messages to Class objects means that you can do what
feels like “meta” stuff in much the same way that you do other things.

David

Maybe you can answer another question. While trying to figure this out I
kept running into scoping issues. The first thing that popped into my
head
was

def go2(arg)
clos = proc {puts “Hi #{arg}”}
class << self
define_method(:hi,clos)
end
end

but this didn’t work because for some reason when I’m inside the scope
of
class << self … end define_method can’t find clos even though it is
inside
the enclosing scope. Is there a reason classes don’t close over their
enclosing scope because I was thinking just like we can have closures we
could also have closures for classes.

Hi –

On Sun, 19 Aug 2007, david karapetyan wrote:

but this didn’t work because for some reason when I’m inside the scope of
class << self … end define_method can’t find clos even though it is inside
the enclosing scope. Is there a reason classes don’t close over their
enclosing scope because I was thinking just like we can have closures we
could also have closures for classes.

The class keyword always starts a new local scope, but class_eval
doesn’t:

x = 1
Object.class_eval { puts x } # 1

class/class_eval is analogous to def/define_method. class and def
start new local scopes; class_eval and define_method don’t.

David

Hi –

On Sat, 18 Aug 2007, Logan C. wrote:

I wouldn’t say it doesn’t exist; it’s just that the lexically inner
definition isn’t scoped to the outer definition. This is relatively
new (1.8.6, if I remember correctly), and made it easier to do
something that was always possible anyway with class_eval +
define_method.

ISTR def foo; def bar; end; end working in 1.8.4 and possibly even
1.8.2. (I didn’t hop aboard the ruby train before 1.8.2 though). I
know I’ve used def foo; def bar before 1.8.6.

I think I meant to type 1.8.4, though I’m semi-guessing anyway. I do
seem to remember that it was an intra-1.8 change, rather than as
between 1.6 and 1.8, but I’m not sure.

David