Ruby Shorthand to Yield Within Blocks
· 1 min read
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.