3

Is it equivalent to define a method in the main environment:

def foo; end

and to define it as Object's instance method:

class Object
  def foo; end
end

or is there a way to distinguish them?

sawa
  • 165,429
  • 45
  • 277
  • 381
  • According to http://stackoverflow.com/questions/917811/what-is-main-in-ruby, any methods defined on `main` are added as instance methods to `Object`. I have a feeling you were looking for something more definitive though. – Linuxios Jan 18 '16 at 20:18
  • @Linuxios The question is whether defining on `main` is different from defining on `Object`. I.e., are they two different things? – sawa Jan 18 '16 at 20:22
  • Yes they are different as to how I am not 100% sure just yet but very basically from irb `def foo; "in main"; end` will allow me to call `foo` anywhere in main because it is actually defined in the context of `main` but `Object.new.foo` will raise `NoMethodError: private method 'foo' called for #`. So it appears when a method is defined in `main` it is privatized for Object. – engineersmnky Jan 18 '16 at 20:25
  • @engineersmnky On my irb, they are not private. Maybe it depends on the version. – sawa Jan 18 '16 at 20:30
  • it seems that `irb` sets up a context and a workspace for you that holds these methods (although given the fact that you say yours are not privatized I am not sure this will work for you) but in irb i can call `self.context.workspace.private_methods` and the `:foo` i defined in the context of `main` is included in here. I am using `2.1.5` – engineersmnky Jan 18 '16 at 20:41
  • @engineersmnky `main` is irrelevant to irb. Without irb, the main environment if `main`. Another way to refer to it is `TOPLEVEL_BINDING.receiver`. – sawa Jan 18 '16 at 20:56
  • I cannot replicate what you are describing as *"they are not private"* (see my post below)?? Seems strange – engineersmnky Jan 18 '16 at 21:47

4 Answers4

2

When you don't specify an object in whose singleton class the method should be defined, i.e. when you don't say

def bar.foo; end

but just

def foo; end

then the method gets defined in what is called the default definee. Normally, the default definee is the lexically enclosing module, but at the top-level, it is Object and the methods become private by default. (The default definee can also be changed by various meta-programming methods.)

So, the two snippets you posted are not equivalent, because the first will define a private method, and the second one a public method:

def foo; end

method(:foo).owner
# => Object

Object.private_instance_methods(false).include?(:foo)
# => true

but

class Object; def foo; end end

method(:foo).owner
# => Object

Object.private_instance_methods(false).include?(:foo)
# => false

Note that in IRb and some other REPLs top-level methods may end up public. That's a well-known incompatibility leaking a private internal implementation detail of those REPLs and not an official part of Ruby's semantics.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • Thanks for the answer. I also read Sonoda's articles in the past, but may have missed that one. – sawa Jan 19 '16 at 00:00
1

For me running a few tests I see methods being defined in the context of main as being set as private_instance_methods of Object not public_methods. This means unless you specifically define a private instance method on Object you should be able to distinguish them.

Strangely I cannot confirm the other posts showing that these are public methods of Object. If anyone has any insight on this I would love to understand. (Maybe it has something to do with the use of pry?)

I get the same in 1.9.3, 2.0.0, 2.1.5, and 2.2.3 so I am unsure why others get a different response.

/scripts/test.rb

puts "RUBY_VERSION: #{RUBY_VERSION}"
puts 'def foo; "in Main";end'
def foo; "in Main";end
puts "foo method response: #{foo}"
puts "Object.private_instance_methods:#{Object.private_instance_methods(false).inspect}"
puts "Trying to call Object.new.foo"
Object.new.foo

(Raw Output):

ruby 1.9.3p551 (2014-11-13) [i386-mingw32]

RUBY_VERSION: 1.9.3
def foo; "in Main";end
foo method response: in Main
Object.private_instance_methods:[:foo]
Trying to call Object.new.foo
/scripts/test.rb:7:in `<main>': private method `foo' called for #<Object:0x640368> (NoMethodError)

ruby 2.0.0p247 (2013-06-27) [x64-mingw32]

RUBY_VERSION: 2.0.0
def foo; "in Main";end
foo method response: in Main
Object.private_instance_methods:[:foo]
Trying to call Object.new.foo
/scripts/test.rb:7:in `<main>': private method `foo' called for #<Object:0x00000002d34b98> (NoMethodError)

ruby 2.1.5p273 (2014-11-13 revision 48405) [i386-mingw32]

RUBY_VERSION: 2.1.5
def foo; "in Main";end
foo method response: in Main
Object.private_instance_methods:[:foo]
Trying to call Object.new.foo
/scripts/test.rb:7:in `<main>': private method `foo' called for #<Object:0x2bec750> (NoMethodError)

ruby 2.2.3p173 (2015-08-18 revision 51636) [i386-mingw32]

RUBY_VERSION: 2.2.3
def foo; "in Main";end
foo method response: in Main
Object.private_instance_methods:[:foo]
Trying to call Object.new.foo
/scripts/test.rb:7:in `<main>': private method `foo' called for #<Object:0x593808> (NoMethodError)
engineersmnky
  • 25,495
  • 2
  • 36
  • 52
  • You are right that there is a difference between visibility, but that is not as crucial as what the owner is. If you define a private method within `Object`, then it will be the same. – sawa Jan 18 '16 at 21:34
  • 1
    @sawa agreed there is no direct specific way I have found to differentiate where the method was added. You could possibly use `method_added` to capture this but I have would have to test that. – engineersmnky Jan 18 '16 at 22:05
  • 2
    "top-level" methods being `public` instead of `private` is one of the well-known incompatibilities of IRb with standard Ruby. There are also some others surrounding the usage of Refinements and the handling of local variables. – Jörg W Mittag Jan 18 '16 at 23:28
  • @JörgWMittag Thank you for the confirmation. Honestly when I read this question my first thought was that I hope you specifically would chime in here since I have seen the depth of knowledge you have with the true inner workings of Ruby. – engineersmnky Jan 19 '16 at 13:51
0

Ruby 2.2.1.

self
#=> main
self == Object
#=> false
Object === self
#=> true

So main is an instance of Object.

main is not equal Object - these are different things.

Defined methods in main and Object

def foo; end
self.class.instance_methods(false)
#=> [:pry, :__binding__, :foo]

class Object
  def bar; end
end  
#=> :bar
self.class.instance_methods(false)
#=> [:pry, :__binding__, :foo, :bar]

are both available for main and new instances of Object:

foo
#=> nil
bar
#=> nil
Object.new.foo
#=> nil
Object.new.bar
#=> nil

EDIT

We can check the owner of the method defined in main (which occurs to be Object):

method(:foo).owner
#=> Object

It kind of implying you can't distinguish between methods defined in main and Object.

On Ruby 2.2.1 it becomes Object's public instance method:

Object.public_method_defined?(:foo)
#=> true
Andrey Deineko
  • 51,333
  • 10
  • 112
  • 145
  • Do you have any explanation as to why I cannot replicate this? I am trying to understand why I show methods defined in the context of `main` as being `private_instance_methods` of `Object`. – engineersmnky Jan 18 '16 at 21:52
  • @engineersmnky: yours is the correct behavior, IRb and some other REPLs are broken/incompatible in this regard. That's well-known and nothing to worry about. – Jörg W Mittag Jan 19 '16 at 00:29
0

In the end, they seem to be equivalent, as Andrey Deineko's answer tells. However, my concern was that defining a method on main should result in defining an instance method on main's singleton class, which is distinct from the class Object. For example, if I define a method on an ordinary object, I can see that it becomes an instance method of its singleton class:

s = ""
def s.bar; end
s.singleton_class.instance_methods(false) # => [:bar]

However, with main, it is different. Defining a method on main does not result in it becoming an instance method of its singleton class. The returned list does not include :foo:

def foo; end
singleton_class.instance_methods(false)
# => [:inspect, :to_s, :source, :kill, :exit, :conf, :context, :irb_quit, :quit, :irb_print_working_workspace, :irb_cwws, :irb_pwws, :cwws, :pwws, :irb_current_working_binding, :irb_print_working_binding, :irb_cwb, :irb_pwb, :irb_chws, :irb_cws, :chws, :cws, :irb_change_binding, :irb_cb, :cb, :workspaces, :irb_bindings, :bindings, :irb_pushws, :pushws, :irb_push_binding, :irb_pushb, :pushb, :irb_popws, :popws, :irb_pop_binding, :irb_popb, :popb, :jobs, :fg, :help]

So it looks like the method jumps the singleton class of main and is directly defined on Object. Hence, they cannot be distinguished.

sawa
  • 165,429
  • 45
  • 277
  • 381