'include <module>' and infinite recursion

Discussion in 'Ruby' started by Brian Candler, Mar 4, 2005.

  1. Hello,

    I am having trouble understanding what's going on here, and I wonder if any
    guru here could enlighten me. I am getting infinite recursion when I don't
    expect it, and it's something to do with 'including' a module at the top
    level.

    $ ruby --version
    ruby 1.8.2 (2004-07-29) [i386-freebsd5]

    Here's a small bit of code which demonstrates the issue:

    ---- 8< ------------------------------------------------------------------
    require 'ostruct'

    module MyApp
    O = OpenStruct.new
    O.testitem = "hello"
    def test
    "test ok"
    end
    def wibble
    return O.wibble # should return nil
    end
    end

    klass = class <<MyApp::O; self; end
    p klass.ancestors
    #==> [OpenStruct, Object, Kernel]

    include MyApp

    klass = class <<MyApp::O; self; end
    p klass.ancestors
    #==> [OpenStruct, Object, MyApp, Kernel]

    p O.testitem #==> "hello"
    p test #==> "test ok"
    p wibble #==> stack level too deep (SystemStackError) !!!
    ---- 8< ------------------------------------------------------------------

    The idea is that I create an object 'MyApp::O', which uses method_missing to
    capture calls to unknown methods (that's how OpenStruct works internally).

    Also in module MyApp is a method 'wibble' that I want to call. It uses the
    value of 'MyApp::O.wibble' in its processing.

    Finally, I do 'include MyApp' to make the method "wibble" available at the
    top level, and then call it.

    At that point, I get an infinite loop. As far as I can tell:
    - I call wibble (= MyApp#wibble)
    - it calls MyApp::O.wibble
    - while searching for the methods inside this object, it finds MyApp#wibble
    and thus recurses, instead of falling through to method_missing.

    So my questions are:

    (1) Why is this method found? In other words, why does including MyApp at
    the top level affect the methods which are visible within MyApp::O ?

    (2) (probably related). After doing 'include MyApp', I see that the
    singleton class of MyApp::O includes MyApp in its ancestors. Why does it
    appear there? I thought that the 'include' operation would only affect the
    'main' object.

    NOW: I have discovered I can solve this problem simply by putting
    "module_function :wibble" after the definition of method wibble. So my next
    question is:

    (3) Why does this fix things? I see that MyApp still appears in the
    ancestors of the singleton class of MyApp::O, but clearly the calling
    sequence must have changed somehow.

    I've read what there is about module_function in PickAxe v2, but it doesn't
    have much to say (p558, p355). As far as I can gather, this should duplicate
    my existing methods, so that they can be called as
    MyApp.test
    MyApp.wibble
    but it doesn't say anything about otherwise changing the behaviour of the
    existing methods that I defined. I couldn't find anything on the RubyGarden
    wiki either.

    Incidentally, I can remove OpenStruct and method_missing from the problem
    set; the resulting code is less practically useful, but still demonstrates
    the issue.

    ---- 8< ------------------------------------------------------------------
    module MyApp
    O = Hash.new
    O[:testitem] = "hello"
    def test
    "test ok"
    end
    def wibble
    return O.wibble # should give 'method not found'
    end
    #module_function :wibble
    end

    include MyApp

    p O[:testitem] #==> "hello"
    p test #==> "test ok"
    p wibble #==> stack level too deep (SystemStackError) !!!
    ---- 8< ------------------------------------------------------------------

    With module_function :wibble commented out, I get
    8:in `wibble': stack level too deep (SystemStackError)

    With module_function :wibble uncommented, I get
    8:in `wibble':private method `wibble' called for {:testitem=>"hello"}:Hash (NoMethodError)

    and again, I don't really understand why I get either of those messages. I
    thought that 'wibble' would not be visible at all to MyApp::O, rather than
    visible as a private method?

    And final related question:

    (4) How/why are the following two definitions different?

    module MyApp
    def wibble; puts "ok"; end
    module_function :wibble
    end
    MyApp.wibble #==> "ok"
    include MyApp
    wibble #==> "ok"
    -----
    module MyApp
    def MyApp.wibble; puts "ok"; end
    end
    MyApp.wibble #==> "ok"
    include MyApp
    wibble #==> undefined local variable or method `wibble' for main:Object (NameError)


    Anyway, I'd much appreciate it if someone could explain, or could point me
    at some documentation which has enough detail about the method search
    mechanism and the semantics of 'include' and 'module_function' that I can
    work it out for myself.

    Many thanks...

    Brian.
     
    Brian Candler, Mar 4, 2005
    #1
    1. Advertising

  2. On Fri, 4 Mar 2005 19:25:21 +0900, Brian Candler <> wrote:
    > Hello,
    >
    > I am having trouble understanding what's going on here, and I wonder if any
    > guru here could enlighten me. I am getting infinite recursion when I don't
    > expect it, and it's something to do with 'including' a module at the top
    > level.
    >
    > [snip]
    > So my questions are:
    >
    > (1) Why is this method found? In other words, why does including MyApp at
    > the top level affect the methods which are visible within MyApp::O ?
    >


    When you are doing a top level include, you include into Object.
    Everything inherits from object, so every object has your function
    now. Thats the reason why methods like puts work, they are private
    methods of Object.

    > (2) (probably related). After doing 'include MyApp', I see that the
    > singleton class of MyApp::O includes MyApp in its ancestors. Why does it
    > appear there? I thought that the 'include' operation would only affect the
    > 'main' object.


    There is a chapter in pickaxe2 about classes and modules, maybe this
    helps? I don't have time to look into it right now.

    >
    > [snip]
    > (4) How/why are the following two definitions different?
    >
    > module MyApp
    > def wibble; puts "ok"; end
    > module_function :wibble
    > end
    > MyApp.wibble #==> "ok"
    > include MyApp
    > wibble #==> "ok"
    > -----
    > module MyApp
    > def MyApp.wibble; puts "ok"; end
    > end
    > MyApp.wibble #==> "ok"
    > include MyApp
    > wibble #==> undefined local variable or method `wibble' for main:Object (NameError)
    >


    The first creates two methods, while the second one creates only one message:
    See:

    ------------------------------------------------- Module#module_function
    module_function(symbol, ...) => self
    ------------------------------------------------------------------------
    Creates module functions for the named methods. These functions may
    be called with the module as a receiver, and also become available
    >> as instance methods to classes that mix in the module. Module
    >> functions are copies of the original, and so may be changed

    independently. The instance-method versions are made private. If
    used with no arguments, subsequently defined methods become module
    functions.


    > Many thanks...


    I'm no guru, but I hope that I could help a bit.

    >
    > Brian.


    another Brian

    --
    Brian Schröder
    http://ruby.brian-schroeder.de/
     
    Brian Schröder, Mar 4, 2005
    #2
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. GB
    Replies:
    0
    Views:
    403
  2. GB
    Replies:
    0
    Views:
    5,154
  3. Patrick Lioi
    Replies:
    7
    Views:
    352
    Beni Cherniavsky
    Aug 19, 2003
  4. Jp Calderone
    Replies:
    2
    Views:
    312
    Alex Martelli
    Nov 10, 2003
  5. Replies:
    6
    Views:
    257
Loading...

Share This Page