How do I marshal a lambda (Proc) in Ruby?
You cannot marshal a Lambda or Proc. This is because both of them are considered closures, which means they close around the memory on which they were defined and can reference it. (In order to marshal them you'd have to Marshal all of the memory they could access at the time they were created.)
As Gaius pointed out though, you can use ruby2ruby to get a hold of the string of the program. That is, you can marshal the string that represents the ruby code and then reevaluate it later.
How do you stringize/serialize Ruby code?
Use Ruby2Ruby
def save_for_later(&block)
return nil unless block_given?
c = Class.new
c.class_eval do
define_method :serializable, &block
end
s = Ruby2Ruby.translate(c, :serializable)
s.sub(/^def \S+\(([^\)]*)\)/, 'lambda { |\1|').sub(/end$/, '}')
end
x = 40
s = save_for_later { |y| x + y }
# => "lambda { |y|\n (x + y)\n}"
g = eval(s)
# => #<Proc:0x4037bb2c@(eval):1>
g.call(2)
# => 42
This is great, but it does not close over free variables (like x
) and serialize them along with the lambda.
To serialize variables also, you can iterate over local_variables
and serialize them as well. The problem, though, is that local_variables
from within save_for_later
accesses only c
and s
in the code above -- i.e. variables local to the serialization code, not the caller. So unfortunately, we must push the grabbing of local variables and their values to the caller.
Maybe this is a good thing, though, because in general, finding all free variables in a piece of Ruby code is undecidable. Plus, ideally we would also save global_variables
and any loaded classes and their overridden methods. This seems impractical.
Using this simple approach, you get the following:
def save_for_later(local_vars, &block)
return nil unless block_given?
c = Class.new
c.class_eval do
define_method :serializable, &block
end
s = Ruby2Ruby.translate(c, :serializable)
locals = local_vars.map { |var,val| "#{var} = #{val.inspect}; " }.join
s.sub(/^def \S+\(([^\)]*)\)/, 'lambda { |\1| ' + locals).sub(/end$/, '}')
end
x = 40
s = save_for_later(local_variables.map{ |v| [v,eval(v)] }) { |y| x + y }
# => "lambda { |y| _ = 40; x = 40;\n (x + y)\n}"
# In a separate run of Ruby, where x is not defined...
g = eval("lambda { |y| _ = 40; x = 40;\n (x + y)\n}")
# => #<Proc:0xb7cfe9c0@(eval):1>
g.call(2)
# => 42
# Changing x does not affect it.
x = 7
g.call(3)
# => 43
How do I deep copy a Proc in Ruby?
Even if clone
would work on Proc
s, it wouldn't help you, because you'd still be calling clone
on the new value of @foo
, not on the previous one like you want.
What you can do instead is just store the old value of @foo
in a local variable that the lambda can close over.
Example:
def augment_foo()
old_foo = @foo
@foo = lambda { |x| x > 1 ? x*x : old_foo[x] }
end
This way old_foo
will refer to the value that @foo
had when augment_foo
was called and everything will work as you want.
When to use lambda, when to use Proc.new?
Another important but subtle difference between procs created with lambda
and procs created with Proc.new
is how they handle the return
statement:
- In a
lambda
-created proc, thereturn
statement returns only from the proc itself - In a
Proc.new
-created proc, thereturn
statement is a little more surprising: it returns control not just from the proc, but also from the method enclosing the proc!
Here's lambda
-created proc's return
in action. It behaves in a way that you probably expect:
def whowouldwin
mylambda = lambda {return "Freddy"}
mylambda.call
# mylambda gets called and returns "Freddy", and execution
# continues on the next line
return "Jason"
end
whowouldwin
#=> "Jason"
Now here's a Proc.new
-created proc's return
doing the same thing. You're about to see one of those cases where Ruby breaks the much-vaunted Principle of Least Surprise:
def whowouldwin2
myproc = Proc.new {return "Freddy"}
myproc.call
# myproc gets called and returns "Freddy",
# but also returns control from whowhouldwin2!
# The line below *never* gets executed.
return "Jason"
end
whowouldwin2
#=> "Freddy"
Thanks to this surprising behavior (as well as less typing), I tend to favor using lambda
over Proc.new
when making procs.
Unable to use Proc/lambda returned from method in select/reject filters in Ruby
You need to remove the colon:
list.select(&valid_transaction)
The & syntax is used to pass a lambda or proc to a method that is expecting a block. That's what you need here.
With a colon, the effect is somewhat different:
list.select(&:valid_transaction)
is equivalent to this:
list.select { |tx| tx.valid_transaction }
Which is not what you intend at all.
The reason the colon has this affect is that Ruby calls #to_proc on the &
argument. A symbol (which is what :valid_transaction
is) responds to #to_proc by creating a proc that calls the method named by the symbol.
Change the binding of a Proc in Ruby
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.
Ruby: marshal and unmarshal a variable, not an instance
There's ObjectSpace._id2ref
:
f = Foo.new #=> #<Foo:0x10036c9b8>
f.object_id #=> 2149278940
ObjectSpace._id2ref(2149278940) #=> #<Foo:0x10036c9b8>
In addition to the caveats about garbage collection ObjectSpace
carries a large performance penalty in jruby (so much so that it's disabled by default)
Is it possible to see the ruby code in a proc?
Take a look at the sourcify gem:
proc { x + y }.to_source
# >> "proc { (x + y) }"
Related Topics
How to Redefine Fixnum's + (Plus) Method in Ruby and Keep Original + Functionality
Installing Ruby-2.1.2: Cannot Load Such File -- Openssl (Loaderror)
Testing Routes with Subdomain Constraints Using Rspec
Simplest Way to Send Raw Byte-Arrays Using Ruby's Tcpsocket-Class
How to Render File in Rails 5 API
Generate Activerecord Schema from an Existing Table
Getting Google File Picker to Work with Drive.File Scope
How to Summarize Array of Integers as an Array of Ranges
How to Ignore a Folder in Zeitwerk for Rails 6
How to Click on a Specific Coordinates of an Element
Does Ruby 1.9.2 Have an Is_A? Function
Regular Expression "Empty Range in Char Class Error"