Ruby Tips & Tricks #1: Procs with Empty Blocks

Hello and welcome to the first Ruby Tips & Tricks post. In this series I would talk about interesting bits in Ruby.

Let’s start the series with a documented, but little known feature of the Proc.new constructor.

When Proc.new is called in a method with attached block, that block will be converted to a Proc object. Sounds confusing? An example shows it best:

def proc_from
  Proc.new
end

proc = proc_from { "hello" }
proc.call  #=> "hello"

When I first ran into code like this, it took me a while to digest it, even though this behaviour is noted pretty early in the Proc documentation.

Now that we know that it exists, how do we use it? Is it better than the more conventional example:

def proc_from(&block)
  block
end

proc = proc_from { "hello" }
proc.call  #=> "hello"

I believe it is! Why? In the conventional example, if there is no block given, block is nil. This contributes to the nil problem:

def conventional_proc_from(&block)
  block
end

def proc_from
  Proc.new
end

conventional_proc_from  #=> nil
proc_from  #=> ArgumentError: tried to create Proc object without a block

In my book, raising an exemption early is way better than dealing with a nil, introduced a couple of levels down the stack. We could have easily fixed the conventional example with a block_given? guard and manually raising ArgumentError, but that would be more convoluted — Proc.new already does that for us.

While the examples above are pretty trivial, if you need to transform the block of a method, why not get it with Proc.new?

If you know the difference between Proc.new, Kernel#proc and Kernel#lambda, a fair question to ask yourself is whether you can get a lambda out of the method block? This is where things get really interesting:

def lamba_from
  lambda
end

hello = lambda_from { |name| "Hello #{name}" }  #=> warning: tried to create Proc object without a block

hello.call  #=> ArgumentError: wrong number of arguments (0 for 1)
hello.call("Genadi")  #=> "Hello Genadi"

While this is converting the proc block to lambda semantics, it produces a confusing warning — warning: tried to create Proc object without a block. The warning carries the same message as the ArgumentError we saw above. It is deliberate and happens only for Kernel#lambda.

With this, I’m ending the first Ruby Tips & Tricks! Do you find Proc.new useful? Know why the Ruby core developers decided that Kernel#lambda should produce a warning? Don’t like this approach at all? Drop a line on twitter at @gsamokovarov.

 
82
Kudos
 
82
Kudos

Now read this

Ruby Tips & Tricks #2: Lambda Literals in 1.9.3

Ruby 1.9.3 support is coming to an end and with it — an end of a syntax limitation. Welcome to the second Ruby Tips & Tricks post. Have you ever wondered why the community style guide suggest you to write lambda literals like... Continue →