Using a module at the toplevel doesn't work

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.

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.

···

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

T.

Hi,

···

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

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.

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

              matz.

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!

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

T.

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.

Logan Capaldo 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" :frowning:

> 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.

···

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

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.

Hi,

···

In message "Re: using a module at the toplevel doesn't work" on Sun, 20 Aug 2006 09:30:11 +0900, "Trans" <transfire@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 wrote:

Logan Capaldo 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" :frowning:

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.

···

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

Hi,

···

In message "Re: using a module at the toplevel doesn't work" on Fri, 18 Aug 2006 02:40:05 +0900, "Trans" <transfire@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.

Yukihiro Matsumoto wrote:

Hi,

>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.

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.

···

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

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

Yukihiro Matsumoto wrote:

Hi,

>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.

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.

···

In message "Re: using a module at the toplevel doesn't work" > on Fri, 18 Aug 2006 02:40:05 +0900, "Trans" <transfire@gmail.com> writes:

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.

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.

Hi,

···

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

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.

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

Yukihiro Matsumoto wrote:

Hi,

>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.

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. :frowning:

Thanks for looking at this,
T.

···

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

>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

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 :wink: 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

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

···

On Aug 17, 2006, at 3:24 PM, 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:

  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