Arrow Lambdas, a Ruby 1.9 Vignette
Proc, the object-y twin of the syntax-level block, is the subject of a controversial change in Ruby 1.9: the addition of a new literal.
It’s here to stay. Let’s play a bit, shall we?
While we still have Kernel#lambda, in Ruby 1.9 the parser now accepts a literal that looks like the following (to support some great new features such as default arguments and associated blocks):
-> { } # the basic form
->(x) { x * 2 } # with an argument
-> x { x * 2} # (without parenthesis, too)
->(x, y) { x * y } # multiple arguments
->(x, y = 2) { x * y } # default arguments
->(x, y, &z) { x * y * z.call } # take a block
These are being called arrow (or pointy) lambdas. It’s just a Proc literal.
Although many people dislike this new syntax, it’s come to grow on me. Having lambdas at the syntax level strikes me as a good functional feature, and functional programming is a hard drug to come off of. I do wish a slightly different format had been used — Erlang makes me want the arguments first, and Haskell makes me want a λ-like backslash (as it does Dave Thomas) — but I can live with it.
Calling them
You can call lambdas using a bit of new syntax, too.
times = ->(x, y) { x * y } times.(2, 3)
This outputs:
6
Make sure you use a dot before the opening parenthesis. If you forget it, you’ll get a NameError when Ruby looks around for a method by that name:
times(2, 3) # BAD!
It raises:
NoMethodError: undefined method `times' for main:Object
I remember this shortend call feature under the moniker dot call (which is reminiscent of regular expression //s dotall, so it works for me, text- processing nerd that I am).
With a block, no less
Let’s try defining a lambda that accepts an associated block when called, since we can these days:
say_hello = ->(n, &name) { n.times { puts "Hello, %s" % name.() } }
Here we assign a lambda to the variable say_hello that takes two arguments; the first a number of times to say something, and the second a block to call to retrieve a name (which will be converted to a lambda with & and assigned to the name block variable).
Now let’s invoke it:
say_hello.(4) do
"Bruce"
end
This outputs:
Hello, Bruce
Hello, Bruce
Hello, Bruce
So, here we call the lambda, sending it 4 as the first argument (which is assigned to n), and a block (which is converted and assigned to name). The sayhello lambda does its work, invoking the name lambda (with no arguments) in turn.
Note we can’t just use yield in the say_hello lambda to call name; yield in this context would make Ruby look for a block passed to the enclosing scope (ie, the method where say_hello is being defined), and that’s not what we want.
It can get really ugly
These new lambda literals can be a bit odd in some syntactic contexts. Here’s one.
As hash values (in old-style hash literals)
Not that this would generally be done anyhow, but boy are the double arrows here ugly:
strategies = {'mult' => ->(x, y = 2) { x * y }}
p strategies['mult'].(3)
Outputs:
6
It’s even worse without parenthesis (it’s a good idea to use parenthesis in any but the most trivial arrow lambda):
{'mult' => -> x, y = 2 { x * y }}
It’s a bit better using the new hash syntax (using a Symbol for a key this time):
{mult: ->(x, y = 2) { x * y}}
Strung out or wrung out
The parser’s pretty good at figuring out what the boundaries of these new lambdas are, and with new possibilities like default arguments, mandatory arguments after default arguments, not to mention block arguments, things can get pretty long and nasty.
The following is a completely legal single lambda being immediately invoked:
-> a = 1, b = 2, c, &d ; e { e = d.(a * b * c); e + 1 }.(3) { |p| p * 4 }
Yeah, I know this lambda is useless. But even if it was useful it’d be too ugly to foist on another human being.
Ruby is not Perl. Say it with me.
Who are we? Who will we be?
Issues like the addition of a new literal really get people riled up (well, as riled up as nerdy backseat language designers get, I guess). This is a language many of us became interested in because of its elegance (perceived or otherwise; it really doesn’t matter), and I think it’s only natural that we try to avoid adding to the language at such a basic level as syntax.
Lambdas are such a basic building block of Ruby, however, that I think a real, flexible literal has been long overdue, and I’m very happy it’s here, especially since it’s in a form I don’t consider completely off-putting.
Hopefully the community will self-regulate a little on convention and continue to consider elegance, not obfuscation, as the defining characteristic of a kickass Rubyist. That’s all any of us can ask for.






