Nothing ventured, nothing gained

a blog by Marc Chung

How Closures Behave In Ruby

»

by Marc Chung

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 proc and Proc.new return different things, and said he would deprecate proc.

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.


  1. ruby 1.8.7 (2008-06-20 patchlevel 22) [i686-darwin9]

  2. jruby 1.1.5 (ruby 1.8.6 patchlevel 114) (2009-01-22 rev 6586) [i386-java]

  3. ruby 1.9.1p0 (2009-01-30 revision 21907) [i386-darwin9]

  4. MacRuby version 0.3 (ruby 1.9.0 2008-06-03) [universal-darwin9.0]

Presentations, Talks, Etc