How Closures Behave In Ruby
»From the ruby part of the brain.
Closures are commonly used to abstract over an expression or a statement.
Ruby gives you–not one–but three ways of creating closures from a block: Using Proc.new, the Kernel#proc method, or the Kernel#lambda method. So what’s the difference between these three techniques?
In Ruby 1.81, Kernel#lambda is an alias for Kernel#proc, so they actually behave identically. Proc.new, on the other hand, has slightly different semantics.
stargate$ cat a.rb
def kernel_proc
proc { return "within proc" }.call
return "kernel_proc"
end
def proc_class
Proc.new { return "within Proc.new" }.call
return "proc_class"
end
def kernel_lambda
lambda { return "within lambda" }.call
return "kernel_lambda"
end
if $0 == __FILE__
puts "return from " + kernel_proc
puts "return from " + proc_class
puts "return from " + kernel_lambda
end
With Ruby 1.8:
stargate$ ruby a.rb
return from kernel_proc
return from within Proc.new
return from kernel_lambda
JRuby 1.1.52 also observes the same semantics:
stargate$ jruby a.rb
return from kernel_proc
return from within Proc.new
return from kernel_lambda
Ruby 1.93 has a different story.
In Ruby 1.9, Kernel#proc will behave identically to Proc.new. The reason behind this is described by David A. Black in this post to ruby-talk:
Matz agreed that it was confusing to have
procandProc.newreturn different things, and said he would deprecateproc.
Eigenclass.org also confirms that proc is now a synonym of Proc.new further describing that both receive their arguments with multiple-assignment (block) semantics.
With Ruby 1.9:
proc{|a,b|}.arity # => 2
proc{|a,b| "bacon"}.call(1) # => "bacon"
Proc.new{|a,b|}.arity # => 2
Proc.new{|a,b| "bacon"}.call(1) # => "bacon"
With Ruby 1.8:
proc{|a,b|}.arity # => 2
proc{|a,b| "bits"}.call(1) # => wrong number of arguments
Proc.new{|a,b|}.arity # => 2
Proc.new{|a,b| "bits"}.call(1) # => "bits"
Unfortunately, this change means that Kernel#proc may lead to undesirable side-effects in your code:
With Ruby 1.9
stargate$ ruby1.9 a.rb
return from within proc
return from within Proc.new
return from kernel_lambda
With MacRuby 0.34
stargate$ macruby a.rb
return from within proc
return from within Proc.new
return from kernel_lambda
When in doubt, go with Kernel#lambda. It’s a safe bet.
Updated: Pending further review, I’ve omitted the bit about Proc.new violating Tennent’s Correspondence Principle.