confused by nested functions

N

Navindra Umanee

Hi,

Why does the first one work but not the second one?

irb(main):001:0> class Test; end
=> nil
irb(main):002:0> def Test.boo; def moo; puts "moo"; end; end
=> nil
irb(main):003:0> Test.boo.moo
moo
=> nil

irb(main):001:0> class Test; def Test.boo; def moo; puts "moo"; end; end; end
=> nil
irb(main):002:0> Test.boo.moo
NoMethodError: undefined method `moo' for nil:NilClass
from (irb):2

Thanks,
Navin.
 
D

Devin Mullins

Navindra said:
Why does the first one work but not the second one?
As mathew said, def is just a command to define a method, it runs when
the interpreter gets to that line. What makes the first one work and the
second one not, however, is a matter of scope. When 'def' is called, it
defines a method in a certain place. That place is determined by what
'class' or 'module' it's in. So class Test; def foo; end end defines a
method on Test objects.

#1: def moo; puts 'moo' end is *not inside a 'class' or 'module' block*.
This is important. You may be defining a class method boo on Test (well,
a singleton method on the Test object), but your @@class_variables won't
actually be class variables of Test, because it's outside the class
block. It's out in the wild. Here's what the PickAxe2 (p. 346) has to
say about that:

"Outside a class or module definition, a definition with an unadorned
method name is added as a private method to class Object, and hence may
be called in any context without an explicit receiver."

So what's happening here?

Test.boo calls the Test.boo method you defined, which defines a private
method moo on Object. The 'def' keyword, as you've seen returns nil. So
Test.boo returns nil. Test.boo.moo tries to invoke the moo method on the
nil object. Since nil.is_a? Object, it has the moo method that you just
defined.

(I'm thinking the PickAxe may be wrong? about the private thing since
you can call #moo with an explicit receiver -- sounds more like public.
Can somebody explain?)

#2: When Test.boo is called, moo is defined as an instance method on
Test. Test.boo returns nil, as before, but nil is not a Test object, so
the moo method does not exist.

---

So... The best way to do what you actually want depends on what you
actually want. Are you just trying to group a bunch of similar methods
inside a subcomponent of Test, or do you want the Test.boo.moo method to
be dynamically generated based on what Test.boo does? If the latter,
then keep in mind that every method call returns an Object. What you
want to do is make Test.boo return an Object that has a moo method on it.

Devin
 
D

David A. Black

Hi --

As mathew said, def is just a command to define a method, it runs when the
interpreter gets to that line. What makes the first one work and the second
one not, however, is a matter of scope. When 'def' is called, it defines a
method in a certain place. That place is determined by what 'class' or
'module' it's in. So class Test; def foo; end end defines a method on Test
objects.

#1: def moo; puts 'moo' end is *not inside a 'class' or 'module' block*. This
is important. You may be defining a class method boo on Test (well, a
singleton method on the Test object), but your @@class_variables won't
actually be class variables of Test, because it's outside the class block.
It's out in the wild. Here's what the PickAxe2 (p. 346) has to say about
that:

"Outside a class or module definition, a definition with an unadorned method
name is added as a private method to class Object, and hence may be called in
any context without an explicit receiver."

So what's happening here?

Test.boo calls the Test.boo method you defined, which defines a private
method moo on Object. The 'def' keyword, as you've seen returns nil. So
Test.boo returns nil. Test.boo.moo tries to invoke the moo method on the nil
object. Since nil.is_a? Object, it has the moo method that you just defined.

(I'm thinking the PickAxe may be wrong? about the private thing since you can
call #moo with an explicit receiver -- sounds more like public. Can somebody
explain?)

I think the private thing depends on lexical scope. Since the nested
definition of moo is not lexically a top-level definition, it isn't
privatized.
#2: When Test.boo is called, moo is defined as an instance method on Test.
Test.boo returns nil, as before, but nil is not a Test object, so the moo
method does not exist.

What I find interesting is this:

class D
def D.boo
def moo
puts "moo"
end
end

def bee
def mee
puts "mee"
end
end
end


D.boo
d = D.new
d.bee

p D.instance_methods.sort # includes both moo and mee


Nesting a definition inside a class method, and nesting a definition
inside an instance method, have exactly the same effect: an instance
method is added to the class. In one case, this means "self"; in the
other, it means "self.class". I'm not sure why it works this way.
Maybe the real rule is "one lexical layer up", which would take both
to the class-definition scope that surrounds them.


David
 
D

Devin Mullins

Yo --
I think the private thing depends on lexical scope. Since the nested
definition of moo is not lexically a top-level definition, it isn't
privatized.

I'm not sure what you mean. Seems lexically top-level to me -- it's not
inside a class or module block. That it's inside a method doesn't seem
to make a difference:

irb(main):001:0> def moo; puts 'moo' end
=> nil
irb(main):002:0> nil.moo
moo
=> nil

You know, if I weren't so !#%@#ing afraid of just reading the @#%#@ing
source code, I'd've had this question answered by now.
What I find interesting is this:

Nesting a definition inside a class method, and nesting a definition
inside an instance method, have exactly the same effect: an instance
method is added to the class. In one case, this means "self"; in the
other, it means "self.class". I'm not sure why it works this way.
Maybe the real rule is "one lexical layer up", which would take both
to the class-definition scope that surrounds them.

Precisely. It works like class variables -- it depends solely on the
*lexical* scope of the method definition. You could have written:

class D
def bee
def mee
puts "mee"
end
end
end
module SomeOtherModule
def D.boo
def moo
puts "moo"
end
end
end

D.boo
d = D.new
d.bee

p (D.instance_methods - D.superclass.instance_methods) # includes only
mee & bee
p SomeOtherModule.instance_methods #includes only moo

And yes, it is interesting. :)

Devin
 
N

Navindra Umanee

Devin Mullins said:
So... The best way to do what you actually want depends on what you
actually want.

Thanks for the careful explanation, Devin.
Are you just trying to group a bunch of similar methods
inside a subcomponent of Test, or do you want the Test.boo.moo method to
be dynamically generated based on what Test.boo does? If the latter,

I was trying to dynamically generate methods within a namespace -- so
essentially the first. For example, I want to generate:

Site.tables.table1()
Site.tables.table2()
Site.pages.page1()
Site.pages.page2()

Except with better names, of course. :)

I guess I should return an Object with the methods I want.

Thanks,
Navin.
 
M

Mark Hubbart

Yo --
=20
David A. Black wrote:
=20
=20
I'm not sure what you mean. Seems lexically top-level to me -- it's not
inside a class or module block. That it's inside a method doesn't seem
to make a difference:
=20
irb(main):001:0> def moo; puts 'moo' end
=3D> nil
irb(main):002:0> nil.moo
moo
=3D> nil

Just a quick note: beware of IRB. when it comes to method and variable
visibility, it doesn't always behave the same as in normal code:

mark@eMac% irb
ruby 1.8.2 (2004-12-25) on powerpc-darwin8.0
Welcome to Interactive Ruby
def foo() :foo end
=3D=3D>nil
nil.foo
=3D=3D>:foo
mark@eMac% ruby <<EOT
heredoc> def foo() :foo end
heredoc> p nil.foo
heredoc> EOT
-:2: private method `foo' called for nil:NilClass (NoMethodError)
mark@eMac%=20
 
D

David A. Black

Hi --

Yo --


I'm not sure what you mean. Seems lexically top-level to me -- it's not
inside a class or module block. That it's inside a method doesn't seem to
make a difference:

irb(main):001:0> def moo; puts 'moo' end
=> nil
irb(main):002:0> nil.moo
moo
=> nil

As Mark says, that's an IRB thing. In Ruby itself:

$ ruby -e 'def x; end; nil.x'
-e:1: private method `x' called for nil:NilClass (NoMethodError)

And, just for reference:

$ ruby -e 'def x; def y; p 1; end; end; x; nil.y'
1

Since def's start a new local scope, I think it's reasonable to
describe "def y" as not being lexically top-level (which I wouldn't if
it were, say, inside an if statement rather than another def). We can
in any case call it "outermost scope". My interpretation is that only
methods whose definitions start in the outermost scope are made into
private methods of Object.


David
 
D

Devin Mullins

Blrgh! Thanks, Mark & David. I have been schooled.
My interpretation is that only
methods whose definitions start in the outermost scope are made into
private methods of Object.

Indeed, that's my impression now, too. I also know now that the magical
top-level Object does not have a singleton class:
a = ""
class << a; def foo; def y; puts 'yo'; end end end
a.foo
a.y
"hello".y
__END__
yo
-:5: undefined method `y' for "hello":String (NoMethodError)

or, well, act like it.

Devin
 
D

David A. Black

Hi --

Blrgh! Thanks, Mark & David. I have been schooled.


Indeed, that's my impression now, too. I also know now that the magical
top-level Object does not have a singleton class:


or, well, act like it.

You're not accessing the top-level object's singleton class there,
though. What you'd want would be:

$ ruby -e 'class << self; def x; p 1; end; end; x'
1


David
 
D

Devin Mullins

David said:
You're not accessing the top-level object's singleton class there,
though. What you'd want would be:

$ ruby -e 'class << self; def x; p 1; end; end; x'
1

I, uh... must be tired. Yeah, that's it. I'm going to shut up now. :)

Devin
confused by the sound of his own voice, apparently...
 

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,183
Messages
2,570,966
Members
47,513
Latest member
JeremyLabo

Latest Threads

Top