Kernel's module methods?

7

7stud --

According to pickaxe2, p516:

Module Kernel
------------
The Kernel module is included by class Object, so its [instance] methods
are available in every Ruby object. The Kernel instance methods are
documented in class Object beginning on page 567. This section
documents the module methods. These methods are called without a
receiver and thus can be called in functional form.
------------


I tried to model that state of affairs with this code:

module K
def K.test1
puts "test1"
end

def test2
puts "test2"
end
end

class O
include K

K.test1

test1
end


--output:--
test1
r2test.rb:16: undefined local variable or method `test1' for O:Class
(NameError)


How come you can call Kernel methods without a receiver?
 
B

Brian Candler

7stud said:
How come you can call Kernel methods without a receiver?

If I understand it rightly: because self is the top level object 'main',
and main is an *instance* of Object.
test2
=> nil
 
J

James Coglan

[Note: parts of this message were removed to make it a legal post.]

2009/8/6 Brian Candler said:
If I understand it rightly: because self is the top level object 'main',
and main is an *instance* of Object.


I think the point being raised is that when you mix a module into a class,
the class gains the module's instance methods, but not its module/singleton
methods. So it seems that the only way you'd be able to call Kernel's module
methods without a receiver would be if `self` were equal to `Kernel`. But
seeing as how you can call these methods sans receiver anywhere in your
program, I assume the Kernel methods are given special treatment and don't
follow the usual scoping rules for method calls.

Another way you could do this is by having all objects inherit from Kernel's
eigenclass, but that's also not allowed in user code so again this points at
special treatment of Kernel code in the Ruby VM.
 
B

Brian Candler

James said:
I think the point being raised is that when you mix a module into a
class,
the class gains the module's instance methods, but not its
module/singleton
methods. So it seems that the only way you'd be able to call Kernel's
module
methods without a receiver would be if `self` were equal to `Kernel`.

Yes, but are you sure they are module methods, not just instance
methods?
But
seeing as how you can call these methods sans receiver anywhere in your
program, I assume the Kernel methods are given special treatment and
don't
follow the usual scoping rules for method calls.

Everywhere in your program, you are inside some instance of Object (or a
subclass of Object). And therefore self is that object, and that object
inherits Kernel's instance methods. e.g.

class Foo
def bar
puts "hello"
end
end

foo = Foo.new
foo.bar

The 'puts' inside method :bar is calling foo.puts. Since you haven't
defined your own instance method 'puts' in the Foo class, then it
follows the class hierarchy back up to Kernel. No special casing
involved.

That is: AFAICS, this would make sense as long as foo were an instance
method of module Kernel, not a module/singleton method.

Also note:

foo.puts #=> private method `puts' called for #<Foo:0x82d875c>

So it seems 'puts' is an instance method of our class, albeit a private
one.

Or have I got this completely wrong?
 
J

James Coglan

[Note: parts of this message were removed to make it a legal post.]

2009/8/6 Brian Candler said:
Yes, but are you sure they are module methods, not just instance
methods?


Yes, for example `puts` is a singleton method on Kernel:

module Kernel
def metaclass
class << self; self; end
end
end

Kernel.metaclass.instance_methods(false).grep /puts/
=> ["puts"]
Kernel.instance_methods(false).grep /puts/
=> []

In fact, `puts` does not even appear as an instance method on Object:

Object.instance_methods.grep /puts/
=> []

But

Everywhere in your program, you are inside some instance of Object (or a
subclass of Object). And therefore self is that object, and that object
inherits Kernel's instance methods. e.g.

class Foo
def bar
puts "hello"
end
end

foo = Foo.new
foo.bar

The 'puts' inside method :bar is calling foo.puts. Since you haven't
defined your own instance method 'puts' in the Foo class, then it
follows the class hierarchy back up to Kernel. No special casing
involved.

That is: AFAICS, this would make sense as long as foo were an instance
method of module Kernel, not a module/singleton method.

Also note:

foo.puts #=> private method `puts' called for #<Foo:0x82d875c>

So it seems 'puts' is an instance method of our class, albeit a private
one.

Or have I got this completely wrong?


It seems privacy might explain it, since that would stop it showing up in my
above example.

class Foo
def p
method :puts
end
end

Foo.new.p
=> #<Method: Foo(Kernel)#puts>

So that `puts` method is coming from Kernel, even though it doesn't appear
in Kernel.instance_methods. This would suggest that all the Kernel module
methods are in fact private instance methods of `Kernel` (making them
callable from any object), but the Kernel module exposes public singleton
versions of all of them. If this is true the Pickaxe explanation is a little
back-to-front.
 
J

James Gray

Yes, but are you sure they are module methods, not just instance
methods?

They are "module functions" actually, implemented with this:

------------------------------------------------- Module#module_function
module_function(symbol, ...) => self
------------------------------------------------------------------------
Creates module functions for the named methods. These functions
may be called with the module as a receiver, and also become
available as instance methods to classes that mix in the module.
Module functions are copies of the original, and so may be changed
independently. The instance-method versions are made private. If
used with no arguments, subsequently defined methods become module
functions.

module Mod
def one
"This is one"
end
module_function :eek:ne
end
class Cls
include Mod
def callOne
one
end
end
Mod.one #=> "This is one"
c = Cls.new
c.callOne #=> "This is one"
module Mod
def one
"This is the new one"
end
end
Mod.one #=> "This is one"
c.callOne #=> "This is the new one"

James Edward Gray II
 
J

James Coglan

[Note: parts of this message were removed to make it a legal post.]

2009/8/6 James Gray said:
They are "module functions" actually, implemented with this:

------------------------------------------------- Module#module_function
module_function(symbol, ...) => self
------------------------------------------------------------------------
Creates module functions for the named methods. These functions
may be called with the module as a receiver, and also become
available as instance methods to classes that mix in the module.
Module functions are copies of the original, and so may be changed
independently. The instance-method versions are made private. If
used with no arguments, subsequently defined methods become module
functions.


Thanks for pointing that out, I wasn't aware of it. Reminds me of a neat
trick for adding a module's instance methods to the same module as singleton
methods:

module MyMod
extend self
end

Any instance methods from MyMod (and modules it includes) will appear as
singleton methods on the MyMod object. It differs from #module_function in
that the methods are not copied, so if you change MyMod#foo, MyMod.foo will
be updated to reflect the new method.
 
J

James Gray

Thanks for pointing that out, I wasn't aware of it. Reminds me of a
neat
trick for adding a module's instance methods to the same module as
singleton
methods:

module MyMod
extend self
end

Any instance methods from MyMod (and modules it includes) will
appear as
singleton methods on the MyMod object. It differs from
#module_function in
that the methods are not copied, so if you change MyMod#foo,
MyMod.foo will
be updated to reflect the new method.

Yes, and I generally prefer extend(self) to module_function after much
time trying it both ways. Unfortunately, I always seem to eventually
run into issues after using module_function, say with constant
resolution or included modules (like you mentioned).

James Edward Gray II
 
B

Brian Candler

James said:
In fact, `puts` does not even appear as an instance method on Object:

Object.instance_methods.grep /puts/
=> []

but:

irb(main):001:0> Object.private_instance_methods.grep(/puts/)
=> ["puts"]
 
J

Joel VanderWerf

James said:
Yes, and I generally prefer extend(self) to module_function after much
time trying it both ways. Unfortunately, I always seem to eventually
run into issues after using module_function, say with constant
resolution or included modules (like you mentioned).

Can you post an example of the constant resolution problem next time you
come across it? I'm curious because I'm in the habit of using
module_function rather than extend(self), not for any good reason except
that it is perhaps more self documenting.
 
J

James Gray

Can you post an example of the constant resolution problem next time
you come across it? I'm curious because I'm in the habit of using
module_function rather than extend(self), not for any good reason
except that it is perhaps more self documenting.

Here's an example I remember from just the other day (though it
probably falls more under the included module problem). Consider this
simple module:

module MoreMath
extend Math # needed for self::sqrt
include Math # needed for self#sqrt and PI

module_function

def circ(r)
2 * PI * r
end

def dist(x1, y1, x2, y2)
sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
end
end

p MoreMath.circ(12)
p MoreMath.dist(0, 0, 3, 4)

Note that I need both an extend and include to get just that much to
work. Or, I can just do:

module MoreMath
include Math
extend self

def circ(r)
2 * PI * r
end

def dist(x1, y1, x2, y2)
sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
end
end

p MoreMath.circ(12)
p MoreMath.dist(0, 0, 3, 4)

Anyway, I too like module_function() and have used it a lot. I like
how it makes the instance methods private so they don't add to an
object's public interface when mixed in.

It just seems like I have more control with extend(self) so I tend to
reach for that now.

James Edward Gray II
 

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,769
Messages
2,569,582
Members
45,070
Latest member
BiogenixGummies

Latest Threads

Top