Blocks
Blocks are closures that can be passed to any method in ruby. A block gets special handling and does not need to be explicitly defined in a methods parameter list.
def dont_tell()
yield "i sleep with a blanky." if block_given?
end
dont_tell do |dirty_secret|
puts dirty_secret
end
dont_tell
In the above example, the method dont_tell is defined and yields
execution back to an implicit block argument via the yield keyword. Line
5 invokes the dont_tell method passing along a block parameter that
accepts a secret as a single parameter and writes it to stdout.
Line 9 also invokes the dont_tell method but without an explicit block parameter.
The call to Kernel#block_given? prevents an attempt to invoke
a block if one is not given.
Block’s can be converted to Proc’s using an & and back to a block using &.
def will_it_blend?(&block)
[1,3,5,7,9,10].each(&block)
end
will_it_blend? do |number|
if number.even?
raise "Boom"
else
print "."
sleep 1
end
end
On line 1 the block is converted to a block, then on line 2 the proc is converted back to a block and passed to each.
Proc
A proc is an object that represents a block of code. A Proc can be created in a couple of ways.
Proc.new { puts "holla!" }
proc do |x|
puts "#{x} here comes the boom!"
end
You can invoke a proc by calling it’s call method.
bomb = proc do |code|
raise "heck" unless code == 42
end
bomb.call(1)
Lambda
A lambda is a special kind of proc. To create one you can invoke Kernel#lambda.
bomb = lambda do |code|
raise "heck" unless code == 42
end
bomb.call(42)
There are two main differences between a proc and a lambda.
- arity
- return
arity
when invoking a lambda the number of parameters passed in must match the number of parameters defined in the definition.
irb(main):009:0> greeter = lambda { |name| puts "hi #{name}" }
=> #<Proc:0x007fe8eb095de0@(irb):9 (lambda)>
irb(main):010:0> greeter.call
ArgumentError: wrong number of arguments (0 for 1)
from (irb):9:in `block in irb_binding'
from (irb):10:in `call'
from (irb):10
from /Users/mo/.rbenv/versions/1.9.3-p545/bin/irb:12:in `<main>'
irb(main):011:0>
procs don’t care.
irb(main):011:0> greeter = proc { |first_name, last_name| puts "hello #{first_name} #{last_name}" }
=> #<Proc:0x007fe8eb07abd0@(irb):11>
irb(main):012:0> greeter.call
hello
=> nil
return
When returning from a lambda, it exits the scope of the lambda back to the caller. Very much like a method invocation.
class Command
def run(number)
callable = lambda do |x|
puts "#{x} entered callable"
return if x.even?
puts "#{x} leaving callable"
end
greet(callable, number)
end
def greet(callable, n)
puts "#{n} entered greet"
callable.call(n)
puts "#{n} leaving greet"
end
end
command = Command.new
command.run(2)
When this example is run returning from the lambda only returns from the lambda. Execution returns to the calling method.
$ ruby blocks.rb
2 entered greet
2 entered callable
2 leaving greet
If we run the same example again, only swap a lambda for a proc. Then we see the following output.
$ ruby blocks.rb
2 entered greet
2 entered callable
When we return from a proc, it exits the method that called the proc.