Change the binding of a Proc in Ruby
ruby
Solution
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.
Problem
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.