bug in nested defs (?)

G

Greg Weeks

When the following code is evaluated

def foo ; "foo" ; end

where does the method go? Here is what I would guess.

If the evaluation context of the def is a class, then the method is an
instance method of the class. If the evaluation context of the def is
not a class, then the method is a singleton method of the context
object.

This rule is ugly, but it almost always works. What I don't understand
is the one exception below. Here are the non-exceptions:

class X
def foo ; "foo" ; end # #method of X
end
X.new.foo # works

obj = []
class <<obj
def foo ; "foo" ; end # singleton method of obj
end
obj.foo # works

class Y
def def_foo
def foo ; "foo" ; end # singleton method of Y instances
end
end
y = Y.new
y.def_foo
y.foo # works

class Z
def Z.def_foo
def foo ; "foo" ; end # #method of Z
end
def_foo
end
Z.new.foo # works

And here is the exception:

module Definer
def def_foo
def foo ; "foo" ; end # class method of User !
end
end
class User
extend Definer
def_foo
end
User.foo # works!
User.new.foo # error!

Bug or feature? In particular, why should Z and User give different
results?

PS: All my talk of "execution context" I picked up off the street. I
don't find it discussed in "Programming Ruby" or "The Ruby Way".
 
R

Ryan Davis

When the following code is evaluated

def foo ; "foo" ; end

where does the method go? Here is what I would guess.

If the evaluation context of the def is a class, then the method is
an
instance method of the class. If the evaluation context of the def
is
not a class, then the method is a singleton method of the context
object.

not quite. see "Defining a Method" of the pickaxe ed 2 on page 330.
 
G

Greg Weeks

Ryan said:
not quite. see "Defining a Method" of the pickaxe ed 2 on page 330.

In my copy, the heading is "Method Definition", and it begins on pg 345.
Are you referring to the paragraph that begins with "Outside a class or
module definition"? That refers to the top-level (I believe). I
omitted that example deliberately since a person who typed it in would
mess up the subsequent examples. Here it is:

def foo ; "foo" ;end
[].foo # works

By the way, I regret my statement that the rule that I stated was ugly.
I thought that I was being self-deprecating about my phrasing of the
rule. But on second thought, neither the phrasing nor the rule is ugly.

However, it doesn't seem to work in the Definer/User example.
 
G

Greg Weeks

Ryan said:
not quite. see "Defining a Method" of the pickaxe ed 2 on page 330.

I now see that you're right. The Pick-axe book explains everything:

"A method definition using an unadorned method name within a class
or module definition creates an instance method".

Here, "within" means lexically within, not dynamically within. This
explains my problem case:

module Definer
def def_foo
def foo ; "foo" ; end # class method of User !
end
end
class User
extend Definer
def_foo
end
User.foo # works!
User.new.foo # error!

The def of foo is dynamically within User, but it is lexically within
Definer. (The fact that it is nested doesn't matter.) Consequently,
foo becomes an instance method of Definer.

This also implies (correctly) that the foo method of y is a common
method, not a singleton method. So I was wrong about that too. But I
think I've got it now. Yippee!
 
P

Phrogz

class Y
def def_foo
def foo ; "foo" ; end # singleton method of Y instances
end
end
y = Y.new
y.def_foo
y.foo # works

Not a singleton method. It's just a delayed definition of a standard
instance method:

class Bar
def def_foo
def foo
"foo"
end
end
end

b1 = Bar.new
b2 = Bar.new
p b1.foo rescue p "error"
#=> "error"

b1.def_foo
p b1.foo
#=> "foo"

p b2.foo
#=> "foo"
 
G

Greg Weeks

After some more thought, the rule that instance methods go into the
lexically enclosing class has surprised me again. Let x = []. I had
thought that the following two methods behaved identically (except for
"foo vs bar", of course):

def x.define_foo
def foo ; "foo" ; end
end

class << x
def define_bar
def bar ; "bar" ;end
end
end

However, in the first case the lexically enclosing class is Object
(well, you know what I mean), and in the second case the lexically
enclosing class is the singleton class of x. So the rule says
(correctly) that the two are different:

x.define_foo
Object.instance_methods.include? "foo" # true
x.singleton_methods.include? "foo" # false

x.define_bar
Object.instance_methods.include? "bar" # false
x.singleton_methods.include? "bar" # true

Good!, I think. But there is still one case where I don't know where
the "def" should go:

x.instance_eval { def baz ; "baz" ; end }

What class acts as the effective enclosing class of the def? Is it
Object, the actual enclosing class? Or Array, the class of x? Or
is it the singleton class of x? The answer is the latter:

x.singleton_methods.include? "baz" # true

Whew!
 
G

Greg Weeks

BTW, as I now understand it, I grabbed the wrong end of the stick when I
concerned myself with nested defs. I'm pretty sure that no good program
does this, not for an inner instance method anyway. "define_method" is
always preferable. (Counterexample, anyone?) In that case, a practical
response to the question of how nested defs behave is to not care. I
*think* that that is the right response. As a newcomer, though, I can't
say for sure.
 
R

Ryan Davis

Not a singleton method. It's just a delayed definition of a standard
instance method:

class Bar
def def_foo
def foo
"foo"
end
end
end

It actually looks pretty cool:

% parse_tree_show -u
# ...
^D
s:)class,
:Bar,
nil,
s:)scope,
s:)defn,
:def_foo,
s:)args),
s:)scope,
s:)block,
s:)defn, :foo, s:)args), s:)scope, s:)block, s:)str,
"foo")))))))))

So the defn foo is just sitting inside. Whenever def_foo gets
executed, the defn inside gets interpreted, defining the new foo in
whatever context it is executing in.
 

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
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top