Getting the binding of the caller

Discussion in 'Ruby' started by Michael Neumann, Aug 17, 2004.

  1. Hi,

    Is there a way to get the binding of the caller?

    class A
    def m(b=binding)
    eval("self", b)
    end
    end

    A.new.m # => #<A:0x81e1bec>
    A.new.m(binding) # => main

    What I want is exactly the second, but without specifying "binding"
    explicitly.

    Possible or not?

    Regards,

    Michael
     
    Michael Neumann, Aug 17, 2004
    #1
    1. Advertising

  2. Michael Neumann wrote:

    > Hi,


    Moin!

    > Is there a way to get the binding of the caller?


    Yup, there is. See the attached file. It's a bit obscure, but it works
    and shouldn't be too slow.

    > Regards,
    > Michael


    More regards,
    Florian Gross


    begin
    require 'simplecc'
    rescue LoadError
    def Continuation.create(*args, &block)
    cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
    result ||= args
    return *[cc, *result]
    end
    end

    # This method returns the binding of the method that called your
    # method. It will raise an Exception when you're not inside a method.
    #
    # It's used like this:
    # def inc_counter
    # Binding.of_caller do |binding|
    # eval("counter += 1", binding)
    # end
    # end
    # counter = 0
    # 2.times { inc_counter }
    # counter # => 2
    #
    # You will have to put the whole rest of your method into the
    # block that you pass into this method. If you don't do this
    # an Exception will be raised. Because of the way that this is
    # implemented it has to be done this way. If you don't do it
    # like this it will raise an Exception.
    def Binding.of_caller(&block)
    old_critical = Thread.critical
    Thread.critical = true
    count = 0
    cc, result, error = Continuation.create(nil, nil)
    error.call if error

    tracer = lambda do |*args|
    type, context = args[0], args[4]
    if type == "return"
    count += 1
    # First this method and then calling one will return --
    # the trace event of the second event gets the context
    # of the method which called the method that called this
    # method.
    if count == 2
    # It would be nice if we could restore the trace_func
    # that was set before we swapped in our own one, but
    # this is impossible without overloading set_trace_func
    # in current Ruby.
    set_trace_func(nil)
    cc.call(eval("binding", context), nil)
    end
    elsif type != "line"
    set_trace_func(nil)
    error_msg = "Binding.of_caller used in non-method context or " +
    "trailing statements of method using it aren't in the block."
    cc.call(nil, lambda { raise(ArgumentError, error_msg ) })
    end
    end

    unless result
    set_trace_func(tracer)
    return nil
    else
    Thread.critical = old_critical
    yield result
    end
    end
     
    Florian Gross, Aug 18, 2004
    #2
    1. Advertising

  3. On Wednesday, August 18, 2004, 8:52:18 AM, Michael wrote:

    > Hi,


    > Is there a way to get the binding of the caller?


    > class A
    > def m(b=binding)
    > eval("self", b)
    > end
    > end


    > A.new.m # => #<A:0x81e1bec>
    > A.new.m(binding) # => main


    > What I want is exactly the second, but without specifying "binding"
    > explicitly.


    > Possible or not?


    It's been discussed on -talk this year, so a search in the archive for
    'binding' should help. Someone posted a continuations-based solution
    that gives you basically what you want, but at considerable runtime
    expense, IIRC.

    There's no plain-Ruby solution; i.e. the language doesn't go out of
    its way to help you here. There's an RCR for it; can't remember which
    one.

    However, blocks can be useful here, and may just be good enough for
    you.

    def trace(&block)
    expr = block.call
    value = eval expr, block
    puts "#{expr} = #{value}"
    end

    def foo
    x = 5
    y = 12
    trace { "Math.sqrt( x**2 + y**2 )" }
    end

    I gotta say that's pretty handy.

    Cheers,
    Gavin

    --
    Gavin's vim tip for the day: ':help text-objects'
     
    Gavin Sinclair, Aug 18, 2004
    #3
  4. Florian Gross wrote:
    > Michael Neumann wrote:
    >> Is there a way to get the binding of the caller?

    >
    >
    > Yup, there is. See the attached file. It's a bit obscure, but it works
    > and shouldn't be too slow.


    Thanks for the quick answer... uhm, but it does seem to behave as it should:

    require 'binding_of_caller'
    class A
    def m
    Binding.of_caller do |binding|
    eval("self", binding)
    end
    end
    end

    p A.new.m # => #<A:0x8115a14>

    I expect "self" to be "main".

    Regards,

    Michael
     
    Michael Neumann, Aug 18, 2004
    #4
  5. Gavin Sinclair wrote:
    > It's been discussed on -talk this year, so a search in the archive for
    > 'binding' should help. Someone posted a continuations-based solution
    > that gives you basically what you want, but at considerable runtime
    > expense, IIRC.
    >
    > There's no plain-Ruby solution; i.e. the language doesn't go out of
    > its way to help you here. There's an RCR for it; can't remember which
    > one.
    >
    > However, blocks can be useful here, and may just be good enough for
    > you.


    Thanks, this trick did it for me. Of course, it's not 100% perfect but
    that doesn't matter.

    Regards,

    Michael
     
    Michael Neumann, Aug 18, 2004
    #5
  6. On Wed, Aug 18, 2004 at 07:52:18AM +0900, Michael Neumann wrote:
    > Hi,
    >
    > Is there a way to get the binding of the caller?
    >
    > class A
    > def m(b=binding)
    > eval("self", b)
    > end
    > end
    >
    > A.new.m # => #<A:0x81e1bec>
    > A.new.m(binding) # => main
    >
    > What I want is exactly the second, but without specifying "binding"
    > explicitly.
    >
    > Possible or not?


    Sure.
    Take a look at Florian Groß' Binding.of_caller at
    http://noegnud.sourceforge.net/flgr/binding_of_caller.rb

    In some contexts, you can avoid the overhead of #callcc, but you get an
    ugly interface in exchange.

    --
    Running Debian GNU/Linux Sid (unstable)
    batsman dot geo at yahoo dot com
     
    Mauricio Fernández, Aug 18, 2004
    #6
  7. Michael Neumann wrote:

    > Thanks for the quick answer... uhm, but it does seem to behave as it
    > should:
    >
    > require 'binding_of_caller'
    > class A
    > def m
    > Binding.of_caller do |binding|
    > eval("self", binding)
    > end
    > end
    > end
    >
    > p A.new.m # => #<A:0x8115a14>
    >
    > I expect "self" to be "main".


    That's weird -- can't find an explanation for it, because the following
    works:

    require 'binding_of_caller'
    this = self
    class A
    def m
    Binding.of_caller do |binding|
    eval("this", binding)
    end
    end
    end

    p A.new.m # => main

    Maybe somebody has the knowledge needed to explain what's going on there?

    > Regards,
    > Michael


    More regards,
    Florian Gross
     
    Florian Gross, Aug 18, 2004
    #7
  8. Florian Gross wrote:
    > Michael Neumann wrote:
    >
    >> Thanks for the quick answer... uhm, but it does seem to behave as it
    >> should:
    >>
    >> require 'binding_of_caller'
    >> class A
    >> def m
    >> Binding.of_caller do |binding|
    >> eval("self", binding)
    >> end
    >> end
    >> end
    >>
    >> p A.new.m # => #<A:0x8115a14>
    >>
    >> I expect "self" to be "main".

    >
    >
    > That's weird -- can't find an explanation for it, because the following
    > works:
    >
    > require 'binding_of_caller'
    > this = self
    > class A
    > def m
    > Binding.of_caller do |binding|
    > eval("this", binding)
    > end
    > end
    > end
    >
    > p A.new.m # => main
    >
    > Maybe somebody has the knowledge needed to explain what's going on there?


    Hm, it still doesn't work in my special case, as I get a
    "Binding.of_caller used in non-method context or trailing statements of
    method using it aren't in the block." exception (and I really don't
    understand how this Binding.of_caller really works ;-):

    require 'binding_of_caller'

    class Decorator
    def initialize(&block)
    @block = block
    end

    def >>(meth_id)
    obj = Binding.of_caller do |binding|
    eval("this", binding)
    end
    old = method(meth_id)
    new = @block.call(old)

    # the line below is why I need the Binding.of_caller
    obj.class.send:)define_method, meth_id, new)
    end
    end

    def wrapwith(value)
    Decorator.new {|old|
    proc {|*args|
    print "##", value, "\n"
    old.call(*args)
    }
    }
    end

    wrapwith(42) >>
    def f(x) x * 2 end

    p f(4)

    ##############

    Regards,

    Michael
     
    Michael Neumann, Aug 18, 2004
    #8
  9. Michael Neumann wrote:

    > Hm, it still doesn't work in my special case, as I get a
    > "Binding.of_caller used in non-method context or trailing statements of
    > method using it aren't in the block." exception (and I really don't
    > understand how this Binding.of_caller really works ;-):


    It works by installing a temporary trace_func. trace_funcs get an event
    on returns from methods and the binding of the context you where in
    before the method returned. Using continuations Binding.of_caller will
    first let method return, grab the binding of the caller, go back to the
    Binding.of_caller call and this time execute the block. If I wouldn't
    raise the exception you mentioned above then statements that aren't in
    the block would be executed twice. This is why there can be no code
    outside of the Binding.of_caller block after the call to it.

    This ought to work, however I find the 'this = self' line quite ugly --
    I still need to find out why just using self doesn't work and if it can
    be solved. (It appears like the binding which I get from the trace_func
    is nested into the original binding which is why variables can be
    accessed, but not the original binding itself which is why self and
    method calls don't work.)

    I couldn't test this code (haven't applied your def -> symbol patch) so
    there might still be a bug in it that I overlooked.

    require 'binding_of_caller'

    class Decorator
    def initialize(&block)
    @block = block
    end

    def >>(meth_id)
    Binding.of_caller do |context|
    obj = eval("this", context)
    old = obj.method(meth_id)
    new = @block.call(old)

    # the line below is why I need the Binding.of_caller
    obj.class.send:)define_method, meth_id, new)
    end
    end
    end

    def wrapwith(value)
    Decorator.new {|old|
    proc {|*args|
    print "##", value, "\n"
    old.call(*args)
    }
    }
    end

    this = self
    wrapwith(42) >>
    def f(x) x * 2 end

    p f(4)

    > Regards,
    > Michael


    More regards,
    Florian Gross
     
    Florian Gross, Aug 18, 2004
    #9
  10. Florian Gross wrote:
    > Michael Neumann wrote:
    >
    >> Hm, it still doesn't work in my special case, as I get a
    >> "Binding.of_caller used in non-method context or trailing statements
    >> of method using it aren't in the block." exception (and I really don't
    >> understand how this Binding.of_caller really works ;-):

    >
    >
    > It works by installing a temporary trace_func. trace_funcs get an event
    > on returns from methods and the binding of the context you where in
    > before the method returned. Using continuations Binding.of_caller will
    > first let method return, grab the binding of the caller, go back to the
    > Binding.of_caller call and this time execute the block. If I wouldn't
    > raise the exception you mentioned above then statements that aren't in
    > the block would be executed twice. This is why there can be no code
    > outside of the Binding.of_caller block after the call to it.


    Thanks for this explanation. Now I understand. It's a bit like a time
    travel ;-)

    > This ought to work, however I find the 'this = self' line quite ugly --
    > I still need to find out why just using self doesn't work and if it can
    > be solved. (It appears like the binding which I get from the trace_func
    > is nested into the original binding which is why variables can be
    > accessed, but not the original binding itself which is why self and
    > method calls don't work.)
    >
    > I couldn't test this code (haven't applied your def -> symbol patch) so
    > there might still be a bug in it that I overlooked.


    This should do it for you:

    this = self
    def f(x) x * 2 end
    wrapwith(42) >> :f

    Regards,

    Michael
     
    Michael Neumann, Aug 18, 2004
    #10
    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. Gavin Kistner

    Binding of the Caller (redux)

    Gavin Kistner, Sep 28, 2004, in forum: Ruby
    Replies:
    7
    Views:
    131
    trans. (T. Onoma)
    Sep 29, 2004
  2. Ben Bleything

    accessing caller's binding implicitly

    Ben Bleything, Nov 10, 2006, in forum: Ruby
    Replies:
    5
    Views:
    125
    Ben Bleything
    Nov 10, 2006
  3. Ken Bloom

    Binding of caller in Ruby 1.9

    Ken Bloom, Apr 11, 2008, in forum: Ruby
    Replies:
    1
    Views:
    131
    Paul Brannan
    Apr 11, 2008
  4. Christopher J. Bottaro
    Replies:
    3
    Views:
    138
    Florian Gilcher
    May 23, 2008
  5. Mark
    Replies:
    2
    Views:
    410
Loading...

Share This Page