Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

using a module at the toplevel doesn't work

0 views
Skip to first unread message

Trans

unread,
Aug 17, 2006, 9:15:27 AM8/17/06
to
Why? Becuase the module uses #define_method, and #define_method isn't
defined in main.

Which brings me to two questions:

1. How do I get around this problem? Please save me from having to
delgate the whole module!

2. Why isn't main a self extended module to begin with? Honestly, this
question has been buigging me for a long time.

T.

Logan Capaldo

unread,
Aug 17, 2006, 9:34:57 AM8/17/06
to

On Aug 17, 2006, at 9:20 AM, Trans wrote:

> Why? Becuase the module uses #define_method, and #define_method isn't
> defined in main.
>
> Which brings me to two questions:
>
> 1. How do I get around this problem? Please save me from having to
> delgate the whole module!
>

irb(main):006:0> case self
irb(main):007:1> when Module, Class
irb(main):008:1> define_method(:a) { 1 }
irb(main):009:1> else
irb(main):010:1* (class << self; self; end).class_eval
{ define_method(:a) { 1 } }
irb(main):011:1> end


> 2. Why isn't main a self extended module to begin with? Honestly, this
> question has been buigging me for a long time.
>

Couldn't tell you.

> T.
>
>


Trans

unread,
Aug 17, 2006, 11:07:55 AM8/17/06
to

Logan Capaldo wrote:
> On Aug 17, 2006, at 9:20 AM, Trans wrote:
>
> > Why? Becuase the module uses #define_method, and #define_method isn't
> > defined in main.
> >
> > Which brings me to two questions:
> >
> > 1. How do I get around this problem? Please save me from having to
> > delgate the whole module!
> >
> irb(main):006:0> case self
> irb(main):007:1> when Module, Class
> irb(main):008:1> define_method(:a) { 1 }
> irb(main):009:1> else
> irb(main):010:1* (class << self; self; end).class_eval
> { define_method(:a) { 1 } }
> irb(main):011:1> end

That's what I want to avoid -- "delegating the whole module" :-(

> > 2. Why isn't main a self extended module to begin with? Honestly, this
> > question has been buigging me for a long time.
> >
> Couldn't tell you.

Matz?

T.

Trans

unread,
Aug 17, 2006, 11:32:31 AM8/17/06
to

Trans wrote:
> Logan Capaldo wrote:
> > On Aug 17, 2006, at 9:20 AM, Trans wrote:
> >
> > > Why? Becuase the module uses #define_method, and #define_method isn't
> > > defined in main.
> > >
> > > Which brings me to two questions:
> > >
> > > 1. How do I get around this problem? Please save me from having to
> > > delgate the whole module!
> > >
> > irb(main):006:0> case self
> > irb(main):007:1> when Module, Class
> > irb(main):008:1> define_method(:a) { 1 }
> > irb(main):009:1> else
> > irb(main):010:1* (class << self; self; end).class_eval
> > { define_method(:a) { 1 } }
> > irb(main):011:1> end
>
> That's what I want to avoid -- "delegating the whole module" :-(

Craggy. It's even worse than that! If I delegate via the singelton of
main then:

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

class C
def q; x; end
end

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

(*frustrated*) It's not the same as defining at the top level.

T.

Yukihiro Matsumoto

unread,
Aug 17, 2006, 12:54:06 PM8/17/06
to
Hi,

In message "Re: using a module at the toplevel doesn't work"

I don't think I understand you. Can you elaborate?

matz.

Pit Capitain

unread,
Aug 17, 2006, 12:55:44 PM8/17/06
to
Trans schrieb:

> Craggy. It's even worse than that! If I delegate via the singelton of
> main then:
>
> class << self
> def x; "x"; end
> end
>
> class C
> def q; x; end
> end
>
> C.new.q
> => NameError: undefined local variable or method `x' for
> #<C:0xb7cfffc4>
>
> (*frustrated*) It's not the same as defining at the top level.

Tom, defining a method at the top level is the same as defining a
private method of Object:

def m
end

is the same as

class Object
private
def m
end
end

What are you trying to do?

Regards,
Pit

Trans

unread,
Aug 17, 2006, 1:35:05 PM8/17/06
to

Yukihiro Matsumoto wrote:

> I don't think I understand you. Can you elaborate?

Sure thing. It seems natural to me that "main" would a module of the
form:

module Main
extend self
end

Such a module provides all the characteristics of the toplevel --def,
include, etc.

Hmmm.... now that I spell it out.... Given that every method defined in
main becomes a private method of Object, this Main object looks a whole
lot like Kernel itself. So maybe that's a better way to think about it:
Why isn;t the toplevel Kernel (w/toplevel methods being private methods
of Kernel)?

T.

Trans

unread,
Aug 17, 2006, 1:44:40 PM8/17/06
to
Hi Capitain,

> What are you trying to do?

Well, I have module called Taskable. It's an emulation of Rake's basic
task pattern. eg.

desc "foo description"

task :foo => [ :foo_prerequisite ] do
...
end

But rather than define tasks globally as with Rake's system. I designed
it to work within modules/classes namspace including the tasks being
actual methods. So the one can do:

class X
task :a => [:b] do
print "a"
end

task :b do
print "b"
end
end

X.new.a; puts
=> ba

Unfortuately when I use this to make a Rake command-line emulator I
want to use the module at the toplevel and can't.

Thanks,
T.

Yukihiro Matsumoto

unread,
Aug 17, 2006, 1:50:38 PM8/17/06
to
Hi,

In message "Re: using a module at the toplevel doesn't work"

on Fri, 18 Aug 2006 02:40:05 +0900, "Trans" <tran...@gmail.com> writes:

|Yukihiro Matsumoto wrote:
|
|> I don't think I understand you. Can you elaborate?
|
|Sure thing. It seems natural to me that "main" would a module of the
|form:
|
| module Main
| extend self
| end
|
|Such a module provides all the characteristics of the toplevel --def,
|include, etc.

Why? If it is really required it's fairly easy to add toplevel
methods like we did for #include.

matz.

Trans

unread,
Aug 17, 2006, 2:24:11 PM8/17/06
to

See what I wrote to Pit Capitain. Even if I define the missing toplevel
methods:

def define_method( meth, &block )
Object.class_eval do
define_method( meth, &block )
private meth
end
end

def ancestors
Object.ancestors
end

include MyModule

I still get errors because state (e.g. instance vars used in MyModule)
are being stored in the toplevel instance of Object, main, and not in
Object. I got methods going one way and instance vars going another.

Is there a reason the toplevel _can't_ be Kernel instead of a specal
instance of Object? Just seems like that would be hek of a lot easier
all around.

Thanks,
T.

Pit Capitain

unread,
Aug 17, 2006, 3:24:51 PM8/17/06
to
Trans schrieb:

> Unfortuately when I use this to make a Rake command-line emulator I
> want to use the module at the toplevel and can't.

Tom, I think I still don't get what your problem is. If you want to use
a module at the toplevel just include it in Object.

The module:

module M
def val *args
if args.empty?
@val
else
@val = args[ 0 ]
end
end
end

I used some instance variables here, because you wrote about them in the
answer to Matz.

Make it available at the toplevel (and everywhere else):

class Object
include M
end

p val # => nil
val "main"
p val # => "main"

It's also available at the class level:

class Q
p val # => nil
val "Q"
p val # => "Q"
end

HTH

Regards,
Pit

Trans

unread,
Aug 17, 2006, 4:18:37 PM8/17/06
to
Pit,

Pit Capitain wrote:
> Trans schrieb:
> > Unfortuately when I use this to make a Rake command-line emulator I
> > want to use the module at the toplevel and can't.
>
> Tom, I think I still don't get what your problem is. If you want to use
> a module at the toplevel just include it in Object.

The module is for module/class level. It uses #define_method and
#ancestors. Including it into Object just gives:

./taskable.rb:184:in `task': undefined method `define_method' for
main:Object (NoMethodError)

Using extend makes the methods not available at the toplevel.

I pasted the code base I'm working on below. To try it, create a task
and call it:

task :foo do
puts "foo"
end

foo

T.


# = taskable.rb
#
# == Copyright (c) 2006 Thomas Sawyer
#
# Ruby License
#
# This module is free software. You may use, modify, and/or
redistribute this
# software under the same terms as Ruby.
#
# This program is distributed in the hope that it will be useful, but
WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS
# FOR A PARTICULAR PURPOSE.
#
# == Auhtor(s)
#
# * Thomas Sawyer

# Author:: Thomas Sawyer
# Copyright:: Copyright (c) 2006 Thomas Sawyer
# License:: Ruby License

require 'tsort'

# = TSort
#
# TSort is used to determine which prerequisites to run
# for a task.
#
# Here we add a convenience method --if such a long method
# name can be said to be convenient ;) NOTE: Although I ended
# up not using this after all --it still may proved useful and
# may eventually be added to Facets or submitted as a patch.

module TSort
def strongly_connected_components_from(node)
result = []
each_strongly_connected_component_from(node) do |component|
result << component
end
result
end
end

# = Task
#
# A Task is a Method with prerequisite dependencies.
#
# NOTE Can't subclass Method b/c Method has no initiator.
# Not sure that would be a good idea anyway.

class Task
attr_accessor :description

def initialize( name, container, preq, desc=nil, &action )
@name = name
@container = container
@prerequisite = preq || []
@description = desc
@action = action
end

def prerequisite
@prerequisite ||= []
end

# #--
# # TODO Problem is 'a' can't take any parameters,
# # and there's no way to sort the tasks
# # from here. FIX if possible.
# #++
# def to_proc
# r = @prerequisite
# n = @name
# a = @action
# lambda {
# r.each{ |d| send(d) unless n == d }
# instance_eval &a if a
# }
# end

def complete
# ensure no cycles in the graph and extract call graph
#own = (class << self; self; end) # in case there are singletons?
t = Graph.new( container, name )
t.each_strongly_connected_component_from( name ) do |c|
#@container.instance_task(c).call
@container.send("->#{d}")
end
end

def call
@action.call
end

def each(&blk)
@prerequisite.each(&blk)
end

# Task::Graph is a Tarjan-sorted hash.
# It is used to generate the proper dependency chains.
# TODO Make Multiton off of base?

class Graph < Hash
include TSort

def initialize( base, name )
@base = base.class
@name = name
instance_map( name )
end

def instance_map( name )
t = @base.instance_task(name)
raise "task does not exist -- #{name} in #{@base}" unless t
self[name] = t.prerequisite || []
self[name].each { |r| instance_map( r ) }
end

alias_method :tsort_each_node, :each_key

def tsort_each_child(node, &block)
fetch(node).each(&block)
end
end

end

# = Taskable
#
# The Taskable module provides a generic task system
# patterned after Rake, but useable in any
# code context --not just with the Rake tool. In other
# words one can create methods with dependencies.
#
# NOTE Unlike methods, tasks can't take parameters
# if they are to be used as prerequisites. They can
# pass on the parameters of the original call though.

module Taskable

# Define description for subsequent task.

def desc(line=nil)
return @last_description unless line
@last_description = line.gsub("\n",'')
end

# Use up the description for subsequent task.

def desc!
l = @last_description
@last_description = nil
l
end

# Define a task.

def task(args, &action)
if Hash === args
raise ArgumentError, "#{args.size} for 1" if args.size != 1
name, *deps = *(args.to_a.flatten)
name = name.to_sym
deps = deps.compact.collect{ |e| e.to_sym }
else
name, deps = args.to_sym, []
end
# create task by creating core and callable methods.
# We do it this way b/c otherwise we'd have to use
# instance_eval and then can't pass paramaters (TODO fix w/Ruby
1.9).
if action
define_method( "->#{name}", &action ) # core of the task
define_method( name ) do |*args|
# ensure no cycles in the graph and extract call graph
#own = (class << self; self; end) # in case there are
singletons?
todolist = Task::Graph.new( self, name )
todolist.each_strongly_connected_component_from( name ) do |c|
send("->#{c}", *args)
end
end
instance_task_table[name] = Task.new( name, self, deps, desc!,
&action )
elsif m = instance_task(name)
#m.description = desc! # TODO should this apply only if there is
an action?
m.prerequisite.concat deps unless deps.empty?
m
else
#define_method( "->#{name}", lambda{} ) # empty action
define_method( name ) do |*args|
# ensure no cycles in the graph and extract call graph
#own = (class << self; self; end) # in case there are
singletons?
todolist = Task::Graph.new( self, name )
todolist.each_strongly_connected_component_from( name ) do |c|
send("->#{c}", *args) unless "#{c}" == "#{name}"
end
end
instance_task_table[name] = Task.new( name, self, deps, desc!,
&action )
end
end

# Access a task.

def instance_task( name )
name = name.to_sym
if instance_task_table and r = instance_task_table[name]
return r
end
ancestors.each do |anc|
itt = anc.instance_task_table
#instance_variable_get("@instance_task_table")
if itt and r = itt[name]
return r
end
end
nil
end

# List of task names.

def instance_tasks( include_ancestors=true )
if include_ancestors and ancestors and ancestors[1]
instance_task_table.keys | ancestors[1].instance_tasks
else
instance_task_table.keys
end
end

protected

def instance_task_table
@instance_task_table ||= {}
end

end

class Module
include Taskable
end

include Taskable

Logan Capaldo

unread,
Aug 17, 2006, 9:24:19 PM8/17/06
to

You are over simplifying Trans.'s problem. He wants dynamic generated
methods scoped to a module, or optionally scoped to the top level.
You can't use #define_method at the top level because instances don't
respond to it. You can't use the toplevel singleton class as I
suggested because a) it's a pain in the neck and b) it doesn't
inherit the right way.


Trans, what about:

# Warning evilness ensuing:

def main.define_method(*args, &block)
Kernel.module_eval{ define_method(*args, &block) }
end

Trans

unread,
Aug 18, 2006, 8:05:58 AM8/18/06
to

Logan Capaldo wrote:

> You are over simplifying Trans.'s problem.

Yea Pit, why are you doing that? ;-)

> He wants dynamic generated
> methods scoped to a module, or optionally scoped to the top level.
> You can't use #define_method at the top level because instances don't
> respond to it. You can't use the toplevel singleton class as I
> suggested because a) it's a pain in the neck and b) it doesn't
> inherit the right way.
>
> Trans, what about:
>
> # Warning evilness ensuing:
>
> def main.define_method(*args, &block)
> Kernel.module_eval{ define_method(*args, &block) }
> end

That's basically what I ended up doing:

def define_method( meth, &block )
Object.class_eval do
define_method( meth, &block )
private meth
end
end

def ancestors
Object.ancestors
end

But as I was saying about the instance variables, it not quite that
simple. I still had to build exceptions into the module code. I just
tried to factor these down to the fewest points of departure as
possible. So I end up with a few areas like this:

$main = self

def instance_task_table
if self == $main
Object.instance_task_table
else
@instance_task_table ||= {}
end
end

and

cont = (self == $main ? Object : self )

Certainly not simple. But so far it appears to be working. I just hope
I've got all the namespace dicing covered.

Thanks,
T.

Trans

unread,
Aug 19, 2006, 8:29:23 PM8/19/06
to

As a follow up to this, I made the following blog post:


http://7rans0noma.blogspot.com/2006/08/vote-kernel-for-toplevel-object.html

T.

Yukihiro Matsumoto

unread,
Aug 21, 2006, 12:28:36 AM8/21/06
to
Hi,

In message "Re: using a module at the toplevel doesn't work"
on Sun, 20 Aug 2006 09:30:11 +0900, "Trans" <tran...@gmail.com> writes:

|http://7rans0noma.blogspot.com/2006/08/vote-kernel-for-toplevel-object.html

I don't feel that making Kernel as toplevel self is not a good idea,
because:

* toplevel def does not define methods on Kernel, but Object.
* toplevel include does not include modules into Kernel, but Object.
* toplevel private etc. do not work on Kernel, but Object.

matz.

Trans

unread,
Aug 21, 2006, 1:07:23 AM8/21/06
to

To keep the namespace separate I take it? In general one can hardly
tell them apart. I know I was very suprised when I discoverd that
Object starts out completely devoid of any methods. And I suspect now,
that toplevel's effect on Object is the reason why.

So by what you say I would be inclined to wonder why isn't toplevel
Object itself rather than a special instance of Object. But the reaon
it cannot be Object is because Object can not 'extend self' like
toplevel must.

So if not Kernel and also not Object, what of the merits of separate
module Main, included in Object.

SomeClass.ancestors => [ Object, Main, Kernel ]

So that what I am trying to undersatnd. Why would you go to the trouble
to make the toplevel a (partial) proxy of Object when a self extended
module would prioved complete and seemless functionality?

Thanks,
T.

Trans

unread,
Aug 21, 2006, 11:36:35 PM8/21/06
to
Well, is there anyway to turn toplevel effects on Object off? I have a
real scenario where it causes issues.

module X
def self.method_missing( name, *args )
if name ...
if require 'foo'
send( name, *args )
...

Now if some method is defined at toplevel it can screw up my module X
lazy loader.

T.

Yukihiro Matsumoto

unread,
Aug 22, 2006, 12:47:11 AM8/22/06
to
Hi,

In message "Re: using a module at the toplevel doesn't work"

Can you show me working (well, I mean non-working) example. I am not
sure how above example relates to toplevel effects.

matz.

Trans

unread,
Aug 22, 2006, 1:01:13 AM8/22/06
to

Sure. I have a bunch of build scripts that can be resused in other
scripts. Typcially people will reuse then threw one-off scripts that
simple define methods at the top level. E.g. something like

def test
Script.test
end

Now the Script module uses lazy loading in order to keep it fast --no
sense in loading module's you don't need:

module Script

extend self

# Some tasks belong to variant scripts.

TASK2SCRIPT = {
:version => :revision,
:changelog => :revision
}

# When a built-in task is called it's script is dynamically loaded.

def method_missing( meth, *args, &blk )
file = TASK2SCRIPT[meth] || meth
begin
require "sake/#{file}"
if respond_to?(meth)
send( meth, *args, &blk )
else
super
end
rescue LoadError
super
end
end

end

As you can see my particlar 'test' example will cause an infinite loop
because the toplevel definition is being added to all objects. :(

Thanks for looking at this,
T.

Matthew Johnson

unread,
Aug 22, 2006, 1:09:22 AM8/22/06
to
> |Well, is there anyway to turn toplevel effects on Object off? I
> have a
> |real scenario where it causes issues.
> |
> | module X
> | def self.method_missing( name, *args )
> | if name ...
> | if require 'foo'
> | send( name, *args )
> | ...
> |
> |Now if some method is defined at toplevel it can screw up my module X
> |lazy loader.
>
> Can you show me working (well, I mean non-working) example. I am not
> sure how above example relates to toplevel effects.
>
> matz.

I would imagine something like this is being described:

def test
puts "didn't work as desired"
end

module X
def self.method_missing name, *args
# check name and require file containing the method
puts "works as desired: resending message #{name}"
end
def self.do_test
test
end
end

X.do_test

Of course if you try X.test of self.test inside of X we get "works as
desired..."

Matthew

Yukihiro Matsumoto

unread,
Aug 22, 2006, 1:31:55 AM8/22/06
to
Hi,

In message "Re: using a module at the toplevel doesn't work"

on Tue, 22 Aug 2006 14:05:06 +0900, "Trans" <tran...@gmail.com> writes:

|As you can see my particlar 'test' example will cause an infinite loop
|because the toplevel definition is being added to all objects. :(

But they are private by default, so that 1.9 send will call
method_missing if you specify the explicit receiver.

matz.

Trans

unread,
Aug 22, 2006, 9:17:32 AM8/22/06
to

Ah, okay so fixing my toplevel #define_method to privatize the method
should do the trick, at least in my case, I see. Thanks. Of course,
just my luck, I also need 1.9's #send that doesn't invoke private
methods now! ;) How do I get around that in 1.8 again?

So #private saves the situation. Hmm... Still, the subtleties involved
here make me wonder, is it worth it? Has the embuing of toplevel
methods throughout all objects proven of significant worth? I knwo I
have never intentially created a toplevel method with the intended
effect of adding it to all objects. And Mathew makes another good case.

Thanks,
T.

Trans

unread,
Aug 22, 2006, 11:33:43 AM8/22/06
to

Trans wrote:
> How do I get around that in 1.8 again?

To answer my own question:

eval %{ Script.#{type}(*args, &data) }

T.

Yukihiro Matsumoto

unread,
Aug 22, 2006, 8:22:05 PM8/22/06
to

In message "Re: using a module at the toplevel doesn't work"
on Tue, 22 Aug 2006 22:20:05 +0900, "Trans" <tran...@gmail.com> writes:

|And Mathew makes another good case.

For his case, replace do_test as

def self.do_test
self.test
end
matz.

Trans

unread,
Aug 22, 2006, 11:45:11 PM8/22/06
to

Almost. But that's sort of reverse the situation. Will we have to add
'self.' to every call b/c there's the chance someone will define a
toplevel method when using our libs?

I think the question stands: of what significant value come from having
the toplevel add it's methods to Object?

If we have to know these subtle tricks to avoid possible gotchas, don't
you think it should be worth the effort? I don't see how it is. I don't
think a good coder is even going to utilze the fact that toplevel
methods embue all objects in the first place.

T.

Matthew Johnson

unread,
Aug 23, 2006, 12:03:34 AM8/23/06
to
> Almost. But that's sort of reverse the situation. Will we have to add
> 'self.' to every call b/c there's the chance someone will define a
> toplevel method when using our libs?

You won't need to add self to every call, only to calls which may end
up in method_missing. If the method is guaranteed to exist in the
object or one of its ancestors self is not necessary to ensure the
expected method is called. Expecting calls to self to end up in
method_missing seems like a pretty specialized case...

Matthew


Trans

unread,
Aug 23, 2006, 9:04:50 AM8/23/06
to

Sure. All of this is a specialized case. It's a specialized case to use
the toplevel to purposefully add methods to Object. Toplevel methods
aren't that common in the first place, except in one-off scripts. But
if you actaully are utilizng the toplevel, for instance in mycase where
end-users are writting mini task scripts, then these specialized cases
come to bare strongly.

So far his is what I have had to deal with:

1. Manually add #define_method, #ancestors, &c. to proxy to Object
class.
2. Be careful b/c Toplevel and Object class 'self' can get mixed-up.
3. Avoid using #send if you're utilizing method_missing (util 1.9)
4. Add 'self.' prefix to internal methods if purposefully invoking
method_missing.

And I wouldn't be suprised if their are others.

So it's not that this is a huge issue, obviously. But why have these
issues at all if it's completely and easily avoidable? Using a self
extended module (Main) for the toplevel avoids all of this. And if, for
some reason, one needs to add their top level methods to all objects,
'class Object; include Main; end' is a very simple solution.

T.

Trans

unread,
Aug 23, 2006, 9:08:36 AM8/23/06
to
s/their/there/

My grandmother hates it when I do that ;-)

T.

Trans

unread,
Aug 23, 2006, 6:48:43 PM8/23/06
to
Also, irb screws it up:

irb(main):001:0> def x ; "x" ; end
=> nil
irb(main):002:0> Object.private_instance_methods(false).sort
=> ["initialize", "irb_binding"]
irb(main):003:0> Object.public_instance_methods(false).sort
=> ["x"]

T.

Yukihiro Matsumoto

unread,
Aug 24, 2006, 4:43:50 AM8/24/06
to
Hi,

In message "Re: using a module at the toplevel doesn't work"

on Thu, 24 Aug 2006 07:50:13 +0900, "Trans" <tran...@gmail.com> writes:

|Also, irb screws it up:

Yes, it is a irb's known bug.

matz.

0 new messages