Ruby tips: Calling lambdas
08 Dec 2013
Lambdas are a powerful way to write anonymous functions (or closures) in Ruby. I wrote about the subtle differences between lambdas and procs here, and this post is about something a little different. I recently discovered a number of different ways to invoke lambdas that I had never seen before, and ended up learning something new about Ruby itself.
Let’s look at a basic example. Here is a lambda that doesn’t do much of anything:
test = lambda do |*args|
p args
end
So we have a lambda test
that prints its arguments when invoked. The most basic example of this would be something like:
test.call(:one, :two) # => prints [:one, :two]
Nothing surprising there - #call
is the standard method for invoking a lambda. We can also invoke lambdas with []
:
test[:three, :four] # => prints [:three, :four]
All was well until I came upon this blog post. Looking at line 12 of the first Ruby example, we see this:
juxt.(:+, max, min).(2, 3, 5, 1, 6, 4)
Ignoring the functionality of the juxt
lambda for now (although the post is highly recommended reading), the syntax caused me to do a double take. juxt.(:+, max, min)
is unlike any Ruby I’ve seen before. A little experimentation reveals that .()
is just syntactic sugar for the #call
method! This means that we can call
test.(:five, :six) # => prints [:five, :six]
How weird is that? As it turns out, it works for things besides lambdas as well. Take a look:
class Example
def call(*args)
p args
end
end
Example.new.(:hello, :world) # => prints [:hello, :world]
For some final weirdness, this StackOverflow answer notes that the following is also valid:
test::(:seven, :eight) # => prints [:seven, :eight]
We’ve seen a number of different ways to equivalently invoke the #call
method on a lambda. I make no assertion about whether or not you should use these in practice, although it’s fascinating to learn about the various intricacies of Ruby. All of these, and more goodies, are tucked away in the Proc documentation here.
Bonus
If you poke around the Proc documentation linked above, you’ll notice that using ===
between a proc and an object will invoke the proc with the object as a parameter. That allows you to do this:
test === :nine # prints [:nine]
While that technically works, I certainly wouldn’t use that in practice. The real reason for this is allowing you to use procs in the when
clause of a case statement, which delegates to ===
. Here’s a better example:
is_even_int = lambda do |num|
num.is_a?(Integer) && num.even?
end
x = 4
case x
when is_even_int
puts 'x is an even integer'
else
puts 'x is not an even integer'
end
This concludes our tour of the myriad ways to invoke lambdas/procs. I’d be interested to hear any examples of ways people are using these in the wild. Happy #call
ing!