ruby-style-guide
ruby-style-guide copied to clipboard
Single- vs multiline lambdas
The style guide currently says
Use the new lambda literal syntax for single line body blocks. Use the lambda method for multi-line blocks. [link]
# bad l = lambda { |a, b| a + b } l.call(1, 2) # correct, but looks extremely awkward l = ->(a, b) do tmp = a * 7 tmp * b / 50 end # good l = ->(a, b) { a + b } l.call(1, 2) l = lambda do |a, b| tmp = a * 7 tmp * b / 50 end
I disagree. Having to change lambda syntax from -> {}
to lambda do; end
when a lambda grows too large for one line seems wrong. For multi-line lambdas, I would prefer using the stabby lambda syntax with a curly-brace block:
# good
l = ->(a, b) {
tmp = a * 7
tmp * b / 50
}
:+1: I support this change. "Use -> {}
all the time" is a simpler rule, it makes it easy to convert to/from one line/multi-line, and it looks good.
While the switch between lambda
and ->
might be a bit too much, using {}
for multi-line blocks would be inconsistent with the rest of the multi-line blocks which are always using do/end
. On the other hand this block doesn't have quite the usual block semantics, so I don't know. I think this lambda literal was a classic example of how you shouldn't do language design... (there are so many of those in Ruby)
I would prefer always using do...end
for multi line blocks
Yeah, using {}
for multi-line lambdas does look better and makes them easier to spot in code but at the cost of inconsistency with the usual ML block semantics.
The question would be 'How many inconsistencies like this we already allow?' either in the style guide (if any) or in the actual language design as @bbatsov points out.
Is the jury still out on this? Looking here doesn't seem to provide a definitive answer: https://github.com/bbatsov/ruby-style-guide/issues/270
I guess it is. :-)
Why does this not use the structure of almost every other block in Ruby (i.e. going from {}
to do
/end
)? Seems pretty foolish.
I think I have to agree with @bbatsov answer here https://github.com/bbatsov/ruby-style-guide/issues/270#issuecomment-43718894
For the reasons described in this blog article, I suggest changing the preferred notation for lambda notation, single line and multiline) to stabby lambda (->
) notation.
Additional context
I will paste the relevant content of the article here so it is colocated with this issue:
Stabby Notation as an Indicator of Preferred and Default Proc Type
In a previous article, "lambdas Are Better Than procs", I proposed that lambdas should be used rather than procs in almost all cases, given that they are safer in terms of argument count checking and return behavior.
So it makes sense that ->
should create a lambda and not a proc. (As an aside, it always puzzles me when people use the term stabby proc, when it creates a lambda.)
One way to look at it is, by using the stabby lambda notation, we are saying "make me Ruby's implementation of an objectless function". This is at a level higher than "make me a lambda" or "make me a proc", and is probably a better interface to the programmer, especially the newer Rubyist.
->
's Picture-Like Notation
The picture-like notation ->
is quite different from the lambda
and proc
forms, because although all result in method calls that create Proc
instances, lambda
and proc
look like method calls, while ->
does not, instead appearing more like a language construct. On the higher level, it really is a language construct, and the fact that a method needs to be called to create a lambda is an implementation detail that should not matter to the programmer.
The striking appearance of ->
says to the reader "take note, something different is happening here, this marks the beginning of a definition of executable code that will probably be called somewhere else". If a picture is worth a thousand words, then a text picture like ->
is worth, well, at least ten.
The Need for Visual Differentiation
Unlike other code in a method, a lambda's code is not called in sequence (unless it is immediately called as a self invoking anonymous function, but this is rare). Also, sometimes a lambda can be used as if it were a nested method, containing lower level code that may be called multiple times in the method in which it was defined. For these reasons, a pictorial indication setting it apart from other code in the method is especially helpful.
Rubocop
Rubocop is a very useful tool for normalizing code style. For better or worse though, Rubocop's defaults constitute implicit recommendations, and deviating from the defaults can require lengthy and contentious team discussions. Because of this potentially high cost of overriding the defaults, it is important that the basis in reasoning for the selection of the default be sound.
Rubocop's default setting for lambdas is to use ->
with lambda one-liners but lambda
for multiline lambdas. While this is not a matter of monumental importance, I believe it's misguided and should be changed.
My guess is that it is intended to mirror the Ruby code block notation convention of {..}
for single line blocks and do...end
for multi-line blocks. However, the code block case is different because the do
and end
are at the end and beginning of the line, respectively (though it is true that if there are arguments they will appear after the do
). Although the indentation of the code block within the lambda do...end
makes it easy to see that something is going on, it is easy to miss the lambda
and assume it is a normal code block. The pictorial nature of ->
reduces this risk.
I believe that the Rubocop default should be changed to prefer (or at minimum permit) ->
in all cases.
Conclusion
Lambdas are, thankfully, first class objects in Ruby. That is, they can be passed to and returned from methods, and can be assigned to variables. This is a pretty major construct, and I believe a special notation (->
), rather than a method name (lambda
) is justified and helpful. While it is true that class
, module
, and def
also mark the beginning of major language constructs, they are likely to be the first token on a line, whereas lambdas are usually assigned to variables or passed to methods or other lambdas, and are not.
The conciseness and pictorial nature of ->
encourage the use of lambdas, and in my opinion, that is a Good Thing. Lambdas are underused in the Ruby community, and many opportunities for cleaner and clearer code are missed.