nested methods and scope

Discussion in 'Ruby' started by Jamis Buck, Jan 2, 2004.

  1. Jamis Buck

    Jamis Buck Guest

    Sorry if this has been rehashed before--I searched the archive and
    didn't find anything that seemed identical to the question I've got.

    Which is: if I define a method inside a method, currently the inner
    method has no access to the outer method's local variables. Is this a
    conscious design decision, or a bug?

    def test_outer( a, b )
    def test_inner( a )
    puts "#{a} and #{b}"
    end

    test_inner( a+b )
    end

    test_outer( 1, 2 )

    I would expect "3 and 2" to be the output, but I get an "undefined local
    variable or method `b'" error from test_inner.

    Can anyone shed some light on this behavior for me, either in favor of
    it, or at the very least explaining it? (Yes, I know I could use a
    block, instead of a nested method... but I would like to know why the
    nested method approach fails.)

    Thanks,

    Jamis

    --
    Jamis Buck


    ruby -h | ruby -e 'a=[];readlines.join.scan(/-(.)\[e|Kk(\S*)|le.l(..)e|#!(\S*)/) {|r| a << r.compact.first };puts "\n>#{a.join(%q/ /)}<\n\n"'
     
    Jamis Buck, Jan 2, 2004
    #1
    1. Advertising

  2. Jamis Buck wrote:
    > Sorry if this has been rehashed before--I searched the archive and
    > didn't find anything that seemed identical to the question I've got.
    >
    > Which is: if I define a method inside a method, currently the inner
    > method has no access to the outer method's local variables. Is this a
    > conscious design decision, or a bug?
    >
    > def test_outer( a, b )
    > def test_inner( a )
    > puts "#{a} and #{b}"
    > end
    >
    > test_inner( a+b )
    > end
    >
    > test_outer( 1, 2 )
    >
    > I would expect "3 and 2" to be the output, but I get an "undefined local
    > variable or method `b'" error from test_inner.
    >
    > Can anyone shed some light on this behavior for me, either in favor of
    > it, or at the very least explaining it? (Yes, I know I could use a
    > block, instead of a nested method... but I would like to know why the
    > nested method approach fails.)


    I can't give much of an explanation of the rationale, but here's a way
    to do what you were probably trying to do:

    def test_outer( a, b )
    self.class.instance_eval do
    define_method :test_inner do
    puts "#{a} and #{b}"
    end
    end

    test_inner( a+b )
    end

    test_outer( 1, 2 ) # ==> prints "1 and 2"

    The reason this works is that #define_method, unlike the def "special
    form", takes a closure. You have to do the "self.class.instance_eval"
    because #define_method is private.
     
    Joel VanderWerf, Jan 2, 2004
    #2
    1. Advertising

  3. Jamis Buck

    Jamis Buck Guest

    Joel VanderWerf wrote:

    >
    > I can't give much of an explanation of the rationale, but here's a way
    > to do what you were probably trying to do:
    >
    > def test_outer( a, b )
    > self.class.instance_eval do
    > define_method :test_inner do
    > puts "#{a} and #{b}"
    > end
    > end
    >
    > test_inner( a+b )
    > end
    >
    > test_outer( 1, 2 ) # ==> prints "1 and 2"
    >
    > The reason this works is that #define_method, unlike the def "special
    > form", takes a closure. You have to do the "self.class.instance_eval"
    > because #define_method is private.
    >


    That's clever -- thanks for the suggestion. :)

    In further research, I think I've discovered why the local environment
    of the declaring method aren't accessible.

    def outer
    def inner
    puts "hello"
    end
    end

    inner #-> displays "hello"

    The inner method is actually not declared within the scope of the
    (apparently) declaring method--it is promoted to exist at the same scope
    level of its declarer, and therefore is not really "inside" the method
    at all. I was hoping for a Pascal-like nesting of methods, which is not
    really what is happening here.

    I will frankly admit that there are no doubt lots of different ways to
    accomplish (more-or-less) what I was wanting to do, but the benefits of
    the way I original submitted (if it would work as I had hoped) are:

    1) the inner method is inside the outer method's scope, and cannot be
    referenced from outside. I could make the 'inner' method private and
    declare it at the object scope, but then other methods of the object
    could invoke it... The way I'm proposing is actually even more
    restrictive than private access, and cannot be circumvented by using the
    'instance_eval' trick.

    2) the inner method could have complete access to the nesting methods
    local environment, including other nested methods (which, incidentally,
    works -- nested methods can call other nested methods in the same scope,
    they just can't access local variables declared at the same scope).

    This is really more of a theoretical question, though. I'm not actually
    attempting to do this for any practical reason, I'm just trying to "push
    the envelope" to see how much Ruby can do. :)

    --
    Jamis Buck


    ruby -h | ruby -e 'a=[];readlines.join.scan(/-(.)\[e|Kk(\S*)|le.l(..)e|#!(\S*)/) {|r| a << r.compact.first };puts "\n>#{a.join(%q/ /)}<\n\n"'
     
    Jamis Buck, Jan 2, 2004
    #3
    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. Steven T. Hatton
    Replies:
    9
    Views:
    484
  2. Ramza Brown
    Replies:
    1
    Views:
    118
  3. Jay McGavren
    Replies:
    2
    Views:
    96
    Jay McGavren
    Apr 11, 2007
  4. Kenneth McDonald
    Replies:
    5
    Views:
    324
    Kenneth McDonald
    Sep 26, 2008
  5. Andrew Falanga
    Replies:
    2
    Views:
    201
    Andrew Falanga
    Nov 22, 2008
Loading...

Share This Page