I’m definitely a lambda-guy. I’m addicted to simplicity, beauty and intelligibility of closures.
He who claims there is syntax clearer than arr.each { |e| … }
, let him throw the first stone at me.
After all yielding is much more of human nature than jumping.
However there are two cases when yield
seems to be hardly used. First is the &
-shorthand to method
within another codeblock:
def uglify items
raise ArgumentException.new unless items.respond_to? :each
items.each { |e| yield e }
end
If we try to shorten the |e| yield e
clause with &
-notation we’ll gain no success, since yield
is
a keyword rather than method of a very superclass (like Object
or Kernel
.) But who cares?
Well, I do. Because there is a second, much more vital situation when yield
sucks. It is re-passing a
codeblock to a subsequent method:
def cleanup
# do some cleanup if block_given?
end
def evaluate_lua
cleanup yield if block_given?
# do actual evaluate
end
The latter will throw an odd error ArgumentError: wrong number of arguments (1 for 0)
.
The cleanup &yield
neither works, if one were curious.
What’s the problem to use explicit &cb
param here? There are two as usual. First is
an aesthetics, which matters a lot in Ruby. The last and not the least is that instantiating
a new Proc
object leads to a surprisingly heavy performance penalty (≈ five times slower.)
Happily, there is a not wide-known feature
of Proc.new
constructor. Being called without a block within a method with an attached block, it
converts that block to the Proc
object. It means that both
def uglify items
raise ArgumentException.new unless items.respond_to? :each
items.each(&Proc.new)
end
and
def cleanup
if block_given?
# do some cleanup if block_given?
end
end
def evaluate_lua
cleanup &Proc.new
# do actual evaluate
end
will work as expected without extra performance penalty.