8

At the top level, method definition should result in private methods on Object, and tests seem to bear this out:

def hello; "hello world"; end Object.private_instance_methods.include?(:hello) #=> true Object.new.send(:hello) #=> "hello world" 

However, the following also works at top level (self.meta is the eigenclass of main):

self.meta.private_instance_methods(false).include?(:hello) #=> true 

It appears that the hello method is simultaneously defined on the eigenclass of main as well as on Object. What's going on? Note that the false parameter to private_instance_methods excludes super class methods from the method list.

3
  • short hand for class << self; self; end Commented Nov 19, 2009 at 6:21
  • @Andrew sorry this is ruby 1.9 Commented Nov 19, 2009 at 6:42
  • If it's Ruby 1.9, why not just make things clearer and use self.singleton_class? Commented Apr 9, 2012 at 13:54

1 Answer 1

9

First of all, this behavior and the underlying reasoning have always existed; it's nothing new to 1.9. The technical reason it happens is because main is special and treated differently than any other object. There's no fancy explanation available: it behaves that way because it was designed that way.

Okay, but why? What's the reasoning for main to be magical? Because Ruby's designer Yukihiro Matsumoto thinks it makes the language better to have this behavior:

Is so, why are top-level methods not made singleton-methods on this object, instead of being pulled in as instance methods on the Object class itself (and hence into all other classes i.e. more namespace pollution than is usually intended). This would still allow top-level methods to call other top-level methods. And if the top-level object were referred to by some constant like Main, then those methods could be called from anywhere with Main.method(...).

Do you really wish to type "Main.print" everywhere?

Further on in the discussion, he explains that it behaves this way because he feels the "assumption is natural."

EDIT:

In response to your comment, your question is aimed at why main's eigenclass seems to report hello as a private instance method. The catch is that none of the top-level functions are actually added to main, but directly to Object. When working with eigenclasses, the instance_methods family of functions always behave as if the eigenclass is still the original class. That is, methods defined in the class are treated as being defined directly in the eigenclass. For example:

class Object private def foo "foo" end end self.send :foo # => "foo" Object.private_instance_methods(false).include? :foo # => true self.meta.private_instance_methods(false).include? :foo # => true class Bar private def bar "bar" end end bar = Bar.new bar.send :bar # => "bar" Bar.private_instance_methods(false).include? :bar # => true bar.meta.private_instance_methods(false).include? :bar # => true 

We can add a method directly to main's eigenclass, though. Compare your original example with this:

def self.hello; "hello world"; end Object.instance_methods.include? :hello # => false self.meta.instance_methods.include? :hello # => true 

Okay, but what if we really want to know that a given function is defined on the eigenclass, not the original class?

def foo; "foo"; end #Remember, this defines it in Object, not on main def self.bar; "bar"; end #This is defined on main, not Object foo # => "foo" bar # => "bar" self.singleton_methods.include? :foo # => false self.singleton_methods.include? :bar # => true 
Sign up to request clarification or add additional context in comments.

3 Comments

Hi, thanks :D Nonetheless it feels like a bug to me that Bar.new.meta.private_instance_methods(false).include?(:bar) should return true for a method defined on Bar. thanks :)
I'm a little surprised by this as well, but most things in Ruby have surprised me at first. With Ruby, it seems the best ideology is live by the sword or die by the sword.
Probably worth mentioning that the second part of this answer doesn't apply to Ruby 2, at least in Ruby 2.5 self.singleton_class.private_instance_methods(false).include?(:foo) will return false

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.