BLOG / 2006-08-17

Main is a DRY Pain

Update, November 2026: Things are better now. Ruby 2.0 gave main its own define_method, and Ruby 3.0 fixed the module inclusion problem (#9573), so a module included into Kernel now reaches every object. But main still isn’t a module. I came back to that idea in Kill the Proxy and Save Toplevel.

I have a module that depends on define_method and ancestors. It works great when I include it in other modules or classes. But if I try including it into the toplevel it fails miserably.

NameError: undefined local variable or method `define_method' for main:Object

That seems fairly peculiar when you consider that defining methods at toplevel is perfectly acceptable. One must then wonder, what is this toplevel thing anyway?

self    #=> main

Okay, it calls itself “main”. Great. But that doesn’t really tell us anything. Let’s check its class:

self.class  #=> Object

Ah. So it’s an instance of Object. An instance of Object! How is that possible? A normal instance of Object and main can’t be exactly the same. Indeed, they are not.

(public_methods - Object.public_instance_methods).sort
#=> ["include", "private", "public"]
singleton_methods
#=> ["include", "private", "public", "to_s"]

Notice include has been defined here specifically for main. So something special’s going on when you include a module into the toplevel. Hmm… What about methods defined at the toplevel? If main is an instance of Object then are they singleton methods? Well, no. Turns out they get “magically” defined as private methods of the Object class itself, and main’s singleton class space is actually something else entirely.

class << self
  def x; "x"; end
end

class Q
  def q; x; end
end

Q.new.q
#=> NameError: undefined local variable or method `x' for #<Q:0xb7ce9a3c>

Which means the sure solution for my problem…

module Kernel
  include MyModule
end

Can you guess?

module M
  def m; "m"; end
end

module Kernel
  include M
end

m
#=> NameError: undefined local variable or method `m' for main:Object

class Q
  def q; m; end
end

Q.new.q
#=> NameError: undefined local variable or method `m' for #<Q:0xb7cbb324>

Lands me square in the face of the Module Inclusion Problem.

All this leads me to two points. First, I’m stuck! There seems to be no solution to my problem other than rewriting a second version of my module specifically for the toplevel. Talk about lack of DRY! And 2) Why in the world isn’t main a self extended module?

Thomas Sawyer · Published
← All posts