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.

1 def dont_tell()
2     yield "i sleep with a blanky." if block_given?
3   end
4 
5   dont_tell do |dirty_secret|
6     puts dirty_secret
7   end
8 
9   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 &.

 1 def will_it_blend?(&block)
 2     [1,3,5,7,9,10].each(&block)
 3   end
 4 
 5   will_it_blend? do |number|
 6     if number.even?
 7       raise "Boom"
 8     else
 9       print "."
10       sleep 1
11     end
12   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.

  1. arity
  2. 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.

comments powered by Disqus