Blocks are syntactic structures in
Ruby; they are not objects, and cannot be manipulated as objects. It is
possible, however, to create an object that represents a block.
Depending on how the object is created, it is called a
proc or a lambda. Procs have
block-like behavior and lambdas have method-like behavior. Both,
however, are instances of class Proc
.
The subsections that follow explain:
How to create Proc
objects
in both proc and lambda forms
How to invoke Proc
objects
How to determine how many arguments a Proc
expects
How to determine if two Proc
objects are the same
How procs and lambdas differ from each other
We’ve already seen one way to create a Proc
object: by associating a block with a
method that is defined with an ampersand-prefixed block argument.
There is nothing preventing such a method from returning the Proc
object for use outside the
method:
# This method creates a proc from a block def makeproc(&p) # Convert associated block to a Proc and store in p p # Return the Proc object end
With a makeproc
method
like this defined, we can create a Proc
object for ourselves:
adder = makeproc {|x,y| x+y }
The variable adder
now refers
to a Proc
object. Proc
objects created in this way are procs,
not lambdas. All Proc
objects have
a call
method that, when invoked,
runs the code contained by the block from which the proc was created.
For example:
sum = adder.call(2,2) # => 4
In addition to being invoked, Proc
objects can be passed to methods,
stored in data structures and otherwise manipulated like any other
Ruby object.
As well as creating procs by method invocation, there are three
methods that create Proc
objects
(both procs and lambdas) in Ruby. These methods are commonly used, and
it is not actually necessary to define a makeproc
method like the one shown earlier.
In addition to these Proc
-creation
methods, Ruby 1.9 also supports a new literal syntax for defining
lambdas. The subsections that follow discuss the methods Proc.new
, lambda
, and proc
, and also explain the Ruby 1.9 lambda
literal syntax.
We’ve already seen Proc.new
used
in some of the previous examples in this chapter. This is the normal
new
method that most classes
support, and it’s the most obvious way to create a new instance of
the Proc
class. Proc.new
expects no arguments, and returns
a Proc
object that is a proc (not
a lambda). When you invoke Proc.new
with an associated block, it
returns a proc that represents the block. For example:
p = Proc.new {|x,y| x+y }
If Proc.new
is invoked
without a block from within a method that does have an associated
block, then it returns a proc representing the block associated with
the containing method. Using Proc.new
in this way provides an
alternative to using an ampersand-prefixed block argument in a
method definition. The following two methods are equivalent, for
example:
def invoke(&b) def invoke b.call Proc.new.call end end
Another technique for creating Proc
objects is with the lambda
method. lambda
is a method of the
Kernel
module, so it behaves like
a global function. As its name suggests, the Proc
object returned by this method is a
lambda rather than a proc. lambda
expects no arguments, but there must be a block associated with the
invocation:
is_positive = lambda {|x| x > 0 }
In Ruby 1.8, the global proc
method is a synonym for lambda
.
Despite its name, it returns a lambda, not a proc. Ruby 1.9 fixes
this; in that version of the language, proc
is a synonym for Proc.new
.
Because of this ambiguity, you should never use proc
in Ruby 1.8 code. The behavior of
your code might change if the interpreter was upgraded to a newer
version. If you are using Ruby 1.9 code and are confident that it
will never be run with a Ruby 1.8 interpreter, you can safely use
proc
as a more elegant shorthand
for Proc.new
.
Ruby 1.9 supports an entirely new syntax for defining lambdas as
literals. We’ll begin with a Ruby 1.8 lambda, created with the
lambda
method:
succ = lambda {|x| x+1}
In Ruby 1.9, we can convert this to a literal as follows:
Replace the method name lambda
with the punctuation ->
.
Move the list of arguments outside of and just before the curly braces.
Change the argument list delimiters from ||
to ()
.
With these changes, we get a Ruby 1.9 lambda literal:
succ = ->(x){ x+1 }
succ
now holds a Proc
object, which we can use just like
any other:
succ.call(2) # => 3
The introduction of this syntax into Ruby was controversial,
and it takes some getting used to. Note that the arrow characters
->
are different from those used in hash literals. If you
squint at the arrow, you may be able to convince yourself that the
greater-than sign is the Greek letter lambda (λ) with its right-hand
leg chopped off, turned into a hyphen and moved to the left!
As with blocks in Ruby 1.9, the argument list of a lambda literal may include the declaration of block-local variables that are guaranteed not to overwrite variables with the same name in the enclosing scope. Simply follow the parameter list with a semicolon and a list of local variables:
# This lambda takes 2 args and declares 3 local vars f = ->(x,y; i,j,k) { ... }
Lambda literals can be declared with argument defaults, just as methods can:
zoom = ->(x,y,factor=2) { [x*factor, y*factor] }
The parentheses around the argument list of a lambda literal are only required if the argument list includes a semicolon and block-local variable names. Otherwise parentheses may be omitted, resulting in a more compact syntax:
succ = ->x { x+1 } zoom = ->x,y,factor=2 { [x*factor, y*factor] }
If the argument list of a lambda literal has parentheses
around it, there must not be a space between the ->
and the open parenthesis.
Lambda parameters and local variables are optional, of course,
and a lambda literal can omit this altogether. The minimal lambda,
which takes no arguments and returns nil
, is the following:
->{}
One benefit of this new syntax is its succinctness. It can be helpful when you want to pass a lambda as an argument to a method or to another lambda:
def compose(f,g) # Compose 2 lambdas ->(x) { f.call(g.call(x)) } end succOfSquare = compose(->x{x+1}, ->x{x*x}) succOfSquare.call(4) # => 17: Computes (4*4)+1
Lambda literals create Proc
objects and are not the same thing as blocks. If you want to pass a
lambda literal to a method that expects a block, prefix the literal
with &
, just as you would
with any other Proc
object. Here
is how we might sort an array of numbers into descending order using
both a block and a lambda literal:
data.sort {|a,b| b-a } # The block version data.sort &->(a,b){ b-a } # The lambda literal version
In this case, as you can see, regular block syntax is simpler.
Procs and lambdas are objects, not methods, and they cannot
be invoked in the same way that methods are. If p
refers to a Proc
object, you cannot invoke p
as a method. But because p
is an object, you can invoke a method of
p
. We’ve already mentioned that the
Proc
class defines a method named
call
. Invoking this method executes
the code in the original block. The arguments you pass to the call
method become arguments to the block,
and the return value of the block becomes the return value of the
call
method:
f = Proc.new {|x,y| 1.0/(1.0/x + 1.0/y) } z = f.call(x,y)
The Proc
class also defines
the array access operator to work the same way as call
. This means that you can invoke a proc
or lambda using a syntax that is like method invocation, where parentheses have been
replaced with square brackets. The proc invocation above, for example, could be
replaced with this code:
z = f[x,y]
Ruby 1.9 offers an additional way to invoke a Proc
object; as an alternative to square
brackets, you can use parentheses prefixed with a period:
z = f.(x,y)
.()
looks like a method
invocation missing the method name. This is not an operator that can
be defined, but rather is syntactic-sugar that invokes the call
method. It can be used with any object
that defines a call
method and is
not limited to Proc
objects.
Ruby 1.9 adds a curry
method
to the Proc
class. Calling
this method returns a curried version of a proc or
lambda. When a curried proc or lambda is invoked with insufficient
arguments it returns a new Proc
object (also curried) with the given arguments applied. Currying is a
common technique in the functional programming paradigm:
product = ->(x,y){ x*y } # Define a lambda triple = product.curry[3] # Curry it, then specify the first argument [triple[10],triple[20]] # => [30,60]: lambda {|w,x,y,z| w+x+y+z}.curry[1][2,3][4] # => 10
The arity of a proc or lambda is the number of arguments it expects.
(The word is derived from the “ary” suffix of unary, binary, ternary,
etc.) Proc
objects have an arity
method that returns the number of
arguments they expect. For example:
lambda{||}.arity # => 0. No arguments expected lambda{|x| x}.arity # => 1. One argument expected lambda{|x,y| x+y}.arity # => 2. Two arguments expected
The notion of arity gets confusing when a Proc
accepts an arbitrary number of
arguments in an *
-prefixed final
argument. When a Proc
allows
optional arguments, the arity
method returns a negative number of the form -n-1
. A return value of this form indicates
that the Proc
requires n
arguments, but it may optionally take
additional arguments as well. -n-1
is known as the one’s-complement of n
, and you can invert it with the ~
operator. So if arity
returns a negative number m
, then ~m
(or -m-1
) gives you the number of required
arguments:
lambda {|*args|}.arity # => -1. ~-1 = -(-1)-1 = 0 arguments required lambda {|first, *rest|}.arity # => -2. ~-2 = -(-2)-1 = 1 argument required
There is one final wrinkle to the arity
method. In Ruby 1.8, a Proc
declared without any argument clause at
all (that is, without any ||
characters) may be invoked with any number of arguments (and these
arguments are ignored). The arity
method returns –1
to indicate that there are no
required arguments. This has changed in Ruby 1.9: a Proc
declared like this has an arity of
0
. If it is a lambda, then it is an
error to invoke it with any arguments:
puts lambda {}.arity # –1 in Ruby 1.8; 0 in Ruby 1.9
The Proc
class
defines an ==
method
to determine whether two Proc
objects are equal. It is important to
understand, however, that merely having the same source code is not
enough to make two procs or lambdas equal to each other:
lambda {|x| x*x } == lambda {|x| x*x } # => false
The ==
method only returns
true
if one Proc
is a clone or duplicate of the
other:
p = lambda {|x| x*x } q = p.dup p == q # => true: the two procs are equal p.object_id == q.object_id # => false: they are not the same object
A proc is the object form of a block, and it behaves like a
block. A lambda has slightly modified behavior and behaves more like a
method than a block. Calling a proc is like yielding to a block,
whereas calling a lambda is like invoking a method. In Ruby 1.9, you
can determine whether a Proc
object
is a proc or a lambda with the instance method lambda?
. This predicate returns true
for lambdas and false
for procs. The subsections that follow
explain the differences between procs and lambdas in detail.
Recall from Chapter 5 that the return
statement returns from the
lexically enclosing method, even when the statement is contained
within a block. The return
statement in a block does not just return from the block to the
invoking iterator, it returns from the method that invoked the
iterator. For example:
def test puts "entering method" 1.times { puts "entering block"; return } # Makes test method return puts "exiting method" # This line is never executed end test
A proc is like a block, so if you call a proc that executes a
return
statement, it attempts to
return from the method that encloses the block that was converted to
the proc. For example:
def test puts "entering method" p = Proc.new { puts "entering proc"; return } p.call # Invoking the proc makes method return puts "exiting method" # This line is never executed end test
Using a return
statement in
a proc is tricky, however, because procs are often passed around
between methods. By the time a proc is invoked, the lexically
enclosing method may already have returned:
def procBuilder(message) # Create and return a proc Proc.new { puts message; return } # return returns from procBuilder # but procBuilder has already returned here! end def test puts "entering method" p = procBuilder("entering proc") p.call # Prints "entering proc" and raises LocalJumpError! puts "exiting method" # This line is never executed end test
By converting a block into an object, we are able to pass that
object around and use it “out of context.” If we do this, we run the
risk of returning from a method that has already returned, as was
the case here. When this happens, Ruby raises a LocalJumpError
.
The fix for this contrived example is to remove the
unnecessary return
statement, of
course. But a return
statement is
not always unnecessary, and another fix is to use a lambda instead
of a proc. As we said earlier, lambdas work more like methods than
blocks. A return
statement in a
lambda, therefore, returns from the lambda itself, not from the
method that surrounds the creation site of the lambda:
def test puts "entering method" p = lambda { puts "entering lambda"; return } p.call # Invoking the lambda does not make the method return puts "exiting method" # This line *is* executed now end test
The fact that return
in a
lambda only returns from the lambda itself means that we never have
to worry about LocalJumpError
:
def lambdaBuilder(message) # Create and return a lambda lambda { puts message; return } # return returns from the lambda end def test puts "entering method" l = lambdaBuilder("entering lambda") l.call # Prints "entering lambda" puts "exiting method" # This line is executed end test
Figure 5-3 illustrated the behavior of the break
statement in a block; it causes the
block to return to its iterator and the iterator to return to the
method that invoked it. Because procs work like blocks, we expect
break
to do the same thing in a
proc. We can’t easily test this, however. When we create a proc with
Proc.new
, Proc.new
is the iterator that break
would return from. And by the time
we can invoke the proc object, the iterator has already returned. So
it never makes sense to have a top-level break
statement in a proc
created with Proc.new
:
def test puts "entering test method" proc = Proc.new { puts "entering proc"; break } proc.call # LocalJumpError: iterator has already returned puts "exiting test method" end test
If we create a proc object with an &
argument to the iterator method,
then we can invoke it and make the iterator return:
def iterator(&proc) puts "entering iterator" proc.call # invoke the proc puts "exiting iterator" # Never executed if the proc breaks end def test iterator { puts "entering proc"; break } end test
Lambdas are method-like, so putting a break
statement at the top-level of a
lambda, without an enclosing loop or iteration to break out of,
doesn’t actually make any sense! We might expect the following code
to fail because there is nothing to break out of in the lambda. In
fact, the top-level break
just
acts like a return
:
def test puts "entering test method" lambda = lambda { puts "entering lambda"; break; puts "exiting lambda" } lambda.call puts "exiting test method" end test
A top-level next
statement
works the same in a block, proc, or lambda: it causes the yield
statement or call
method that invoked the block, proc,
or lambda to return. If next
is
followed by an expression, then the value of that expression becomes
the return value of the block, proc, or
lambda.
redo
also works the same in procs and lambdas: it transfers
control back to the beginning of the proc or lambda.
retry
is never allowed in
procs or lambdas: using it always results in a LocalJumpError
.
raise
behaves the same in
blocks, procs, and lambdas. Exceptions always propagate up the call
stack. If a block, proc, or lambda raises an exception and there is
no local rescue
clause, the
exception first propagates to the method that invoked the block with
yield
or that invoked the proc or
lambda with call
.
Invoking a block with yield
is similar to, but not the same as, invoking a method. There are
differences in the way argument values in the invocation are
assigned to the argument variables declared in the block or method.
The yield
statement uses
yield semantics, whereas method invocation uses
invocation semantics. Yield semantics are
similar to parallel assignment and are described in Passing Arguments to a Block. As you might expect, invoking a proc uses yield semantics
and invoking a lambda uses invocation semantics:
p = Proc.new {|x,y| print x,y } p.call(1) # x,y=1: nil used for missing rvalue: Prints 1nil p.call(1,2) # x,y=1,2: 2 lvalues, 2 rvalues: Prints 12 p.call(1,2,3) # x,y=1,2,3: extra rvalue discarded: Prints 12 p.call([1,2]) # x,y=[1,2]: array automatically unpacked: Prints 12
This code demonstrates that the call
method of a proc handles the
arguments it receives
flexibly: silently discarding extras, silently adding nil
for omitted arguments, and even
unpacking arrays. (Or, not demonstrated here, packing multiple
arguments into a single array when the proc expects only a single
argument.)
Lambdas are not flexible in this way; like methods, they must be invoked with precisely the number of arguments they are declared with:
l = lambda {|x,y| print x,y } l.call(1,2) # This works l.call(1) # Wrong number of arguments l.call(1,2,3) # Wrong number of arguments l.call([1,2]) # Wrong number of arguments l.call(*[1,2]) # Works: explicit splat to unpack the array
3.133.151.220