As a follow up to my last post on the "pain that is main", I want to offer a potential improvement for Ruby 2.0. I approached the topic on
ruby-talk this week and while Matz initially took some interest, he hasn't followed up since his last comment:
matz: Why? If it is really required it's fairly
easy to add toplevel methods like we did for #include.
But it isn't always easy. In order get my Taskable module to work, for instance, I had to make exceptions for the toplevel case, which is far from ideal and is fragile [Ed- in fact I'm still getting bugs that I haven't yet pinned down]. These subtile difficulties arise becuase
main
acts as a partial proxy for the Object class. Anyone who has created a proxy object before knows the subtile issues that can come into play. In this case, only the bare minimal interface is supported --essentially the method #include. Yet, even if we take matz' advice and add in all the missing proxy methods, we still won't be 100% out of the woods. The Object class and main are fundamentally two distinct objects --
self
is not the same, nor are their singleton classes, &c. In the vast majority of cases this will never present an issue, but the distinction can creep in. Here's an highlight of one way it can:
module Q
define_method :q do
base = self
define_method :r do
base == self.class
end
end
end
class C
extend Q
q
end
c = C.new
p c.r
# per matz' direction
def define_method( name, &act )
Object.class_eval {
define_method( name, &act )
private name
}
end
extend Q
q
p r
produces
true
false
So in effect Ruby is mildly schizophrenic. The
false
reading is because
main != Object
. So, you can't neccessarily create a DSL for
Object
to be used in
main
, and you can't neccessairly create a DSL for
main
to be used in any Object. Hence the devolution to DRYless code.
There is a potentially elegant solution however, and I'd really like to understand others insights into this (esspecially Matz' of course): Instead of
main
being a special proxy object, just let it be a self extended module.
module Main
extend self
# programs are written as if in here
end
This would provide all the facilities required of the toplevel without all the proxy troubles. Also, while I'm not so convinced of the merits of every toplevel method becoming a private method of all objects (and with Main that can be easily prevented), it has proven workable in practice so it's not a significant factor of consideration here. Main can simply be include in Object to achieve that effect.
class Object
include Main
end
But when we do that it becomes very clear what Main appears to be: Kernel. That strikes me as esspecially interesting. Then again, there may be good reasons to keep the Kernel as a separate module, in which case we'd just have a class hierachy:
Object.ancestors
=> [Object, Main, Kernel]
Nevertheless, it is clear the Kernel could just as well serve as the toplevel object, which, IMHO, makes this an elegant proposition to consider. Perhaps I'll start a campaign as November elections roll around: "Vote Kernel for Toplevel Object!" ;-)