Note that for blocks, {}
and do ... end
are interchangeable. For brevity, only the former will be listed here.
Pre-1.9, lambda
and proc
are synonyms. Essentially the difference is between proc
and block
.
def method(&block) p block.class; p block.inspect; end
l = lambda { 5 }
>> method(&l)
Proc
"#<Proc:0x000000010f493030@(irb):2>"
=> nil
>> method { 5 }
Proc
"#<Proc:0x000000010f487d20@(irb):4>"
=> nil
Since 1.9, lambda
is a special kind of proc
, observable in inspect:
def method(&block) p block.class; p block.inspect; end
l = lambda { 5 }
>> method(&l)
Proc
"#<Proc:0x007fdb128ac3a8@(irb):2 (lambda)>"
=> "#<Proc:0x007fdb128ac3a8@(irb):2 (lambda)>"
>> method { 5 }
Proc
"#<Proc:0x007fdb1289f888@(irb):4>"
=> "#<Proc:0x007fdb1289f888@(irb):4>"
- are not objects and cannot be assigned to variables
- method does not have a variable to reference the block, it is not a parameter, only
yield
andKernel#block_given?
can 'see' the block yield
keyword itself is not really a method/variable- use the
block_given?
method to determine if a block was passed, otherwise, aLocalJumpError
will be raised when you try toyield
without a block - but: much faster! huge performance benefit (for certain Ruby implementations, ie MRI)
- note that a
return
within a block will return from the enclosing method - cons: can only pass in one block to a method
# using an implicit block + yield
def calculate(a, b)
yield(a, b) if block_given?
end
>> calculate(2, 3)
=> nil
>> calculate(2, 3) { |a, b| a + b }
=> 5
# return from within a block returns from the surrounding method
def calculate(a, b)
yield(a, b)
return 10
end
>> calculate(2, 3) { |a, b| return 7 }
=> 7
# (note that this method needs to be called from inside a method
# if you are in irb, as you can't return from the `main` context)
To stick to the principle of least surprise, just don't ever use return
inside a block.
Aside: Object
mixes in Kernel
, that's why Kernel
methods are like global functions, so Kernel#block_given?
is accessible.
- is an object of class
Proc
- made by having the method's last parameter start with & like
&blah
- you can explicitly pass in a Proc:
method(&ref_to_a_block)
- you can also implicitly pass in a block
{}
, Ruby converts it to a Proc for you - the
&blah
parameter is a Proc object, or nil if no block is passed in
# &block is an explicit (named) parameter
def calculation(a, b, &block)
block.call(a, b)
end
puts calculation(5, 5) { |a, b| a + b }
- unlike lambdas, the arguments are flexible. Excess arguments are silently discarded, missing/shortfall of arguments is made up with
nil
.
- are objects, just like everything else in Ruby (of class
Proc
) - can be assigned to variables
- resolves to an expression just like a method (the return value / last expression)
- can take parameters just like a method
- unlike Procs, the arguments must exactly match the definition
# resolves to an expression
lamb = lambda { 'Do or do not' }
>> lamb.call
=> "Do or do not"
# accepts parameters
lamb2 = lambda { |x| x + 1 }
>> lamb2.call 2
=> 3
Lambdas can be received as an explicit parameter.
# can use a lambda as an explicit parameter
def calculate(a, b, operation)
operation.call(a, b)
end
>> calculate(2, 3, lambda { |a, b| a + b })
=> 5
Object#method(sym)
Assuming 1.9+, and by 'blocks' we mean implicit blocks without &blah
parameter in method:
... | block | Proc | lambda |
---|---|---|---|
creating one | tack on { ... } after method invocation |
Proc.new { ... } |
lambda { ... } |
is an object? | no | yes, Proc | yes, special Proc |
can be assigned to variable? | no | yes | yes |
how to pass to method? | tack on after method invocation | for the &blah parameter, either tack on a block after method invocation to get it implicitly converted to a Proc or pass in a variable pointing to a Proc prefixed with & as the last parameter. otherwise pass in as a normal variable pointing to the Proc |
pass in as a normal variable pointing to the lambda |
how to invoke inside method? | yield only |
for the &blah parameter you can either use yield or blah.call . for normal parameters you must use widget.call |
widget.call |
number of arguments | flexible, excess is discarded, shortfall made up with nil |
flexible, excess is discarded, shortfall made up with nil |
must match |
what if you return from inside? |
terminates enclosing method and returns the value specified inside block (acts like code snippet) | terminates enclosing method and returns the value specified inside Proc (acts like code snippet) | return value specified inside lambda to enclosing method, which continues running. (acts like a method) |
how many can be passed into a method? | at most one, using {} |
using &blah parameter (whether using {} or a variable at invocation), at most one. using parameters, many |
using parameters, many |
Where:
pp = Proc.new { |a, b| a * b }
ll = lambda { |a, b| a * b }
Thing \ Receiver | calculate(a, b) yield(a, b) |
calculate(a, b, &block) yield(a, b) OR block.call(a, b) |
calculate(a, b, block) block.call(a, b) |
---|---|---|---|
Block | `calculate(2, 3) { | a, b | a * b }` |
Proc | calculate(2, 3, &pp) |
calculate(2, 3, &pp) |
calculate(2, 3, pp) |
Lambda |
calculate(2, 3, &ll) |
calculate(2, 3, &ll) |
calculate(2, 3, ll) |
- when a method expects an explicit block parameter, you can pass it a block
{}
- when a method expects an implicit block, you can pass it a block variable
&variable_name
# yield calls an implicit (unnamed) block
def calculation(a, b)
yield(a, b)
end
addition = lambda {|x, y| x + y}
puts calculation(5, 5, &addition)
- RubyMonk Ruby Primer – Chapter 7
- RubyMonk Ruby Primer: Ascent – Chapter 0
- Paul Cantrell's old write-up Closures in Ruby (???)
- r/ruby Ruby newbie - I don't understand yield (Sep 2011)
- Reactive.IO blog Understanding Ruby Blocks, Procs and Lambdas (Dec 2008)
- Erik Trautman's Ruby Explained: Blocks, Procs, and Lambdas, aka "Closures"
This is a super helpful guide - thanks for sharing this! I have a clarifying question. What do you mean by the 5th bullet under Implicit Blocks?