— Mar 21, 2016
Update: I found a Gist that does a really great job of summarizing this.
The other week teaching Ruby at Big Nerd Ranch, I made a surprising (to me) discovery. Turns out, Ruby blocks are not a thing. That is, they’re not an object. They’re syntax. Allow me to explain…
Anytime the subject of callables comes up in Ruby, you immediately think “ah yes, blocks, procs, and lambdas”. This paints the mental picture of 3 separate but equal concepts. Not quite. It’s all procs. Blocks are just Ruby syntax.
When referencing callables, there are only procs, i.e. instances of the Proc
class.
Lambdas are a special type of proc.
Specifically lambdas are a slightly more restrictive type of proc.
They exhibit two behaviors not found in regular procs.
Quick! Let’s remind ourselves what this means.
Lambdas raise an ArgumentError
if the wrong number is given.
l = lambda { |a, b| }
# => #<Proc:0x007f94fc386038@(irb):9 (lambda)>
l.call
# ArgumentError: wrong number of arguments (0 for 2)
Procs do not.
p = proc { |a, b| puts "proc!" }
# => #<Proc:0x007f94fc237290@(irb):3>
p.call
# proc!
# => nil
Lambdas can explicitly return from their execution.
def call_lambda
value = lambda { return :ah_hah }.call
puts value
return :still_returned
end
call_lambda
# ah_hah
# => :still_returned
Procs return from the calling context.
def call_proc
proc { return :this_one }.call
return :not_this_one
end
call_proc
# => :this_one
Turns out the stuff we refer to as blocks in Ruby are just the syntax for a special kind of argument that defines a sequence of statements.
These blocks are then used to carry out that operation at a later point.
Actually you’ve already used blocks several times in the above examples.
The methods proc
and lambda
require a block argument used to define their behavior.
In fact, when you use the explicit block syntax, &block_arg
this block argument is used to initialize a Proc
which may be called during the method’s execution.
Let’s illustrate this using pry.
Consider this method with an explicit block argument.
def foo(&b)
binding.pry
end
[3] pry(main)> foo {}
From: (pry) @ line 4 Object#foo:
3: def foo(&b)
=> 4: binding.pry
5: end
pry> b
=> #<Proc:0x007fb96aa3cd90@(pry):6>
There it is, a proc.
As far as I can tell the explicit block argument syntax &b
is just sugar for b = Proc.new
.
We can illustrate that with this method.
def foo
b = Proc.new
b.call
end
foo { "orly" }
# => "orly"
The Ruby docs state that “Proc::new
may be called without a block only within a method with an attached block, in which case that block is converted to the Proc object”.
Interestingly when you invoke the block argument using the yield
method, Ruby is able to optimize the block call pretty significantly.
Check out these benchmarks.
So to answer the question “what is a Ruby block?”.
It’s just the syntax for the special type of argument that is used to define some sequence of behavior.
The only way for you to interact “directly” with a block at all is with the yield
method, but if you wish to pass them around you’ve got to convert it to a proc for reference.
And this may be slow.