I have this code:
l = lambda { a } def some_function a = 1 end
I just want to access a
by the lambda and a special scope which has defined a
already somewhere like inside some_function
in the example, or just soon later in the same scope as:
l = lambda { a } a = 1 l.call
Then I found when calling l
, it is still using its own binding but not the new one where it was called.
And then I tried to use it as:
l.instance_eval do a = 1 call end
But this also failed, it is strange that I can't explain why.
I know the one of the solution is using eval
, in which I could special a binding and executing some code in text, but I really do not want to use as so.
And, I know it is able to use a global variable or instance variable. However, actually my code is in a deeper embedded environment, so I don't want to break the completed parts if not quite necessary.
I have referred the Proc
class in the documentation, and I found a function names binding
that referred to the Proc
's context. While the function only provided a way to access its binding but cannot change it, except using Binding#eval
. It evaluate text also, which is exactly what I don't like to do.
Now the question is, do I have a better (or more elegant) way to implement this? Or using eval
is already the regular manner?
Edit to reply to @Andrew:
Okay, this is a problem which I met when I'm writing a lexical parser, in which I defined a array with fixed-number of items, there including at least a Proc
and a regular expression. My purpose is to matching the regular expressions and execute the Procs under my special scope, where the Proce will involved some local variables that should be defined later. And then I met the problem above.
Actually I suppose it is not same completely to that question, as mine is how to pass in binding to a Proc rather than how to pass it out.
@Niklas: Got your answer, I think that is what exactly I want. It has solved my problem perfectly.
There are only two main differences. First, a lambda checks the number of arguments passed to it, while a proc does not. This means that a lambda will throw an error if you pass it the wrong number of arguments, whereas a proc will ignore unexpected arguments and assign nil to any that are missing.
To keep track of the current scope, Ruby uses bindings, which encapsulate the execution context at each position in the code. The binding method returns a Binding object which describes the bindings at the current position.
Procs are objects, blocks are notA proc (notice the lowercase p) is an instance of the Proc class. This lets us call methods on it and assign it to variables. Procs can also return themselves. In contrast, a block is just part of the syntax of a method call.
You can try the following hack:
class Proc def call_with_vars(vars, *args) Struct.new(*vars.keys).new(*vars.values).instance_exec(*args, &self) end end
To be used like this:
irb(main):001:0* lambda { foo }.call_with_vars(:foo => 3) => 3 irb(main):002:0> lambda { |a| foo + a }.call_with_vars({:foo => 3}, 1) => 4
This is not a very general solution, though. It would be better if we could give it Binding
instance instead of a Hash and do the following:
l = lambda { |a| foo + a } foo = 3 l.call_with_binding(binding, 1) # => 4
Using the following, more complex hack, this exact behaviour can be achieved:
class LookupStack def initialize(bindings = []) @bindings = bindings end def method_missing(m, *args) @bindings.reverse_each do |bind| begin method = eval("method(%s)" % m.inspect, bind) rescue NameError else return method.call(*args) end begin value = eval(m.to_s, bind) return value rescue NameError end end raise NoMethodError end def push_binding(bind) @bindings.push bind end def push_instance(obj) @bindings.push obj.instance_eval { binding } end def push_hash(vars) push_instance Struct.new(*vars.keys).new(*vars.values) end def run_proc(p, *args) instance_exec(*args, &p) end end class Proc def call_with_binding(bind, *args) LookupStack.new([bind]).run_proc(self, *args) end end
Basically we define ourselves a manual name lookup stack and instance_exec
our proc against it. This is a very flexible mechanism. It not only enables the implementation of call_with_binding
, it can also be used to build up much more complex lookup chains:
l = lambda { |a| local + func(2) + some_method(1) + var + a } local = 1 def func(x) x end class Foo < Struct.new(:add) def some_method(x) x + add end end stack = LookupStack.new stack.push_binding(binding) stack.push_instance(Foo.new(2)) stack.push_hash(:var => 4) p stack.run_proc(l, 5)
This prints 15, as expected :)
UPDATE: Code is now also available at Github. I use this for one my projects too now.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With