yield and Enumerable are the crux to building idiomatic Ruby APIs. I love them so much.
Non-Ruby developer here. Is there any difference between a Ruby yield and just passing in a lambda as an argument?
Yes, though there are a few aspects in play here. First, a block parameter is a special argument and in order to pass a lambda in as a block parameter you must use the & operator (this operator will attempt to coerce non-proc objects to procs, so this is why symbols are sometimes used in this way).
When you have a block in ruby, the lifetime of the lexical context is tied to the call-frame that is created upon invocation. This might sound like an implementation detail but it allows the runtime to avoid allocating a proc object to hold the closed over lexical environment (aka. faster and lighter). Generally, if you know you’re receiving a block, it can be an advantage to stick with yield and the implicit parameter rather than pull out the proc as a value and use #call (or an alias of #call like #) on the object (this has been optimized more recently in some cases to allow lazy allocation of the object if you only forward it to another call).
The other difference is around parameter behavior. Blocks and Procs don’t require the arity of the block and the yield to match. So you can take fewer or more arguments than are passed and the extra fields will either be dropped or set to nil. Lambdas however, require the arity to match, much like a method call (distinctly constructed using lambda or -> “stabby” syntax). This can be a good thing but I’ll avoid writing half an article here.
One more advanced case that many are unaware of, you can capture a proc using the constructor without a block, so these end up working in a similar way:
def a_is_42(a, &callable)
callable.call if a == 42 && callable
Proc.new.call if b == 42 && block_given?
Of course, it’s a contrived example on the second because we could just use yield there, but it does show that
we can build the proc object lazily which can be a big win in some hot paths for Ruby code. There are some more optimization techniques but this gives a little taste of the range of differences between the proc world and the block world. If people are curious, I can write up more about this stuff.
TL;DR, you can avoid a lot of allocation and call overhead by keeping things in block form.
That makes sense, thanks!
The only difference is that your lambda will be an instance of the class Proc which is in charge of storing and executing the piece of code associated to your lambda.
It’s a bit more complicated than this, but let’s keep it simple if you’re not familiar with Ruby. ;-)
I invite you to read this article to dive into the specificities of the Proc class and lambdas in Ruby.
My bad! :-)
Agree with you. Sidekiq is a great example of this. :-)
They’re also very useful for app configuration and DSLs.