What are the behavioural differences between the following two implementations in Ruby of the thrice
method?
module WithYield
def self.thrice
3.times { yield } # yield to the implicit block argument
end
end
module WithProcCall
def self.thrice(&block) # & converts implicit block to an explicit, named Proc
3.times { block.call } # invoke Proc#call
end
end
WithYield::thrice { puts "Hello world" }
WithProcCall::thrice { puts "Hello world" }
By "behavioural differences" I include error handling, performance, tool support, etc.
The behavioral difference between different types of ruby closures has been extensively documented
I found that the results are different depending on whether you force Ruby to construct the block or not (e.g. a pre-existing proc).
Gives the results:
If you change
do_call(&existing_block)
todo_call{}
you'll find it's about 5x slower in both cases. I think the reason for this should be obvious (because Ruby is forced to construct a Proc for each invocation).Here's an update for Ruby 2.x
I got sick of writing benchmarks manually so I created a little runner module called benchable
Output
I think the most surprising thing here is that
bench_yield
is slower thanbench_proc
. I wish I had a little more of an understanding for why this is happening.BTW, just to update this to current day using:
On Intel i7 (1.5 years oldish).
Still 2x slower. Interesting.
I think the first one is actually a syntactic sugar of the other. In other words there is no behavioural difference.
What the second form allows though is to "save" the block in a variable. Then the block can be called at some other point in time - callback.
Ok. This time I went and did a quick benchmark:
The results are interesting:
This shows that using block.call is almost 2x slower than using yield.
The other answers are pretty thorough and Closures in Ruby extensively covers the functional differences. I was curious about which method would perform best for methods that optionally accept a block, so I wrote some benchmarks (going off this Paul Mucur post). I compared three methods:
&Proc.new
yield
in another blockHere is the code:
Performance was similar between Ruby 2.0.0p247 and 1.9.3p392. Here are the results for 1.9.3:
Adding an explicit
&block
param when it's not always used really does slow down the method. If the block is optional, do not add it to the method signature. And, for passing blocks around, wrappingyield
in another block is fastest.That said, these are the results for a million iterations, so don't worry about it too much. If one method makes your code clearer at the expense of a millionth of a second, use it anyway.