Would I be correct in thinking this is a similar argument those put forward by GvR against the desirability of multi-line lambdas in Python? i.e. once you need multiple lines then you probably should be naming them.
There's the argument that implementation details should be hidden, therefore separate named functions should be defined and their behaviour should be clear from the name. In my experience, those who go too far in this direction may not be used to first class functions; for example, I worked on a PHP project which dated way before the addition of closures, and even their JS had no lambdas: all functions, even trivial one-liners which were only used once, were given top-level, globally-scoped names, defined all together in a separate file.
There's the argument that each expression shouldn't do too much, hence pulling a lambda out into a (locally scoped) name.
There's the argument that variables which are only used once should be written inline to avoid the mental cost of indirection.
There's the argument that values should have the tightest possible scope.
There's the argument that common 'utility functions' should be pulled out to a separate place.
In situations like this, I usually favour doing the 'real work' in a locally scoped function, and writing the argument-plumbing inline. That lies somewhere in between the author's "labelled_lambda" (tightly scoped definitions, but conflates processing with argument juggling) and "isolated_functions" (separates processing from argument juggling, but has a wider scope).
Ah, sorry you're right; I mustn't have played with Erlang enough to fade out the punctuation (unlike Lisp, where https://github.com/tarsius/paren-face can do it for me ;) ).
Sometimes I do wish Erlang had something like F#'s active patterns because it'd make this sort of thing trivial to add to standard pattern expressions. Maybe someday I'll figure out how to add it myself.
I agree with the sentiment but a better example should have been chosen. The first example given is very easy to read and not at all confusing while the last is much more verbose and hence harder to parse for non-experts.
I see that for serious Erlangers it would make a lot of sense but for people like me who only dabble in it the first is probably as easy or even easier to read than the rest. And the ones using list comprehension are the worst whether they use named functions or not.
> Which versions force your eyes to do less jumping around?
Maybe I'm weird but the author's first examples are easier to read. Having labels is good when there is a non-trivial nested mess, but nothing comes for free. When the idiom is well-understood, being overly explicit ends up with more noise. In the first examples, I'm looking at 1 paragraph of code and understand what's going on. In the supposedly improved examples, labels force a mental dereferencing and a jump to different code paragraphs. Sometimes I don't want to scroll throughout the file (or lookup references) to understand something that's logically part of the same unit of processing.
Of course if the lamdas end up doing a lot of work then the overall structure gets lost and a label would be the right thing to do. But code is no exception to the rule "write for your audience". In this case, the audience is the compiler and humans which are versed in the idioms and style of that environment.
The labeled forms give my brain a chunk of code, with a name, to compartmentalize / internalize. In the nested forms, I have to either chunk it on the fly (every time I need to read it) or not chunk it and make my eyes do a bunch of work.
Code that comes later in a single code base will usually copy the patterns that are already established in that code base.
(Assuming the starting patterns are decent, this is as it should be.)
If you add one more level of fun nesting or something like that, the labeled examples become even clearer. Since it's entirely likely that new code will copy the existing pattern, I prefer code shapes that will grow cleanly.
FWIW HN does not do markdown[0], let alone markdown extensions like triple-backtick code blocks.
[0] it implements a very small subset of markdown badly: emphasis (with no escape so it tends to break when you try to use multiplication signs in your comments) and indented "code" blocks
meh. I've been doing functional programming for 30 years. Just because some Ruby people have trouble understanding it doesn't mean we should water everything down for "inclusivity."
There might be a good alternative. Sometimes it's that one function right in the case expression that prevents lifting the matching to the function head (which is considered good style in Erlang).
To do this you could try wrapping the list:foreach itself rather than worry about the inner function, leading to something which seems more concise and in one piece, leaving the more generic iteration to a well named function:
21 comments
[ 3.0 ms ] story [ 49.1 ms ] threadAllowing multiple lines, by definition, allows breaking up those lines.
* It's not actually one-line, it's one expression; Python's just picky about where you're allowed to break up an expression over multiple lines. For example see http://softwareengineering.stackexchange.com/questions/99243...
GvR just does not like functional programming very much, so Python has peculiar name spaces and second class lambdas.
There's the argument that implementation details should be hidden, therefore separate named functions should be defined and their behaviour should be clear from the name. In my experience, those who go too far in this direction may not be used to first class functions; for example, I worked on a PHP project which dated way before the addition of closures, and even their JS had no lambdas: all functions, even trivial one-liners which were only used once, were given top-level, globally-scoped names, defined all together in a separate file.
There's the argument that each expression shouldn't do too much, hence pulling a lambda out into a (locally scoped) name.
There's the argument that variables which are only used once should be written inline to avoid the mental cost of indirection.
There's the argument that values should have the tightest possible scope.
There's the argument that common 'utility functions' should be pulled out to a separate place.
In situations like this, I usually favour doing the 'real work' in a locally scoped function, and writing the argument-plumbing inline. That lies somewhere in between the author's "labelled_lambda" (tightly scoped definitions, but conflates processing with argument juggling) and "isolated_functions" (separates processing from argument juggling, but has a wider scope).
PS: Having only dabbled in Erlang, to me the real problem exposed by those examples is the lack of a combined lambda/case, e.g. as (eventually) found in Haskell https://unknownparallel.wordpress.com/2012/07/09/the-long-an...
All lambda in Erlang is case:
Sometimes I do wish Erlang had something like F#'s active patterns because it'd make this sort of thing trivial to add to standard pattern expressions. Maybe someday I'll figure out how to add it myself.
I see that for serious Erlangers it would make a lot of sense but for people like me who only dabble in it the first is probably as easy or even easier to read than the rest. And the ones using list comprehension are the worst whether they use named functions or not.
Maybe I'm weird but the author's first examples are easier to read. Having labels is good when there is a non-trivial nested mess, but nothing comes for free. When the idiom is well-understood, being overly explicit ends up with more noise. In the first examples, I'm looking at 1 paragraph of code and understand what's going on. In the supposedly improved examples, labels force a mental dereferencing and a jump to different code paragraphs. Sometimes I don't want to scroll throughout the file (or lookup references) to understand something that's logically part of the same unit of processing.
Of course if the lamdas end up doing a lot of work then the overall structure gets lost and a label would be the right thing to do. But code is no exception to the rule "write for your audience". In this case, the audience is the compiler and humans which are versed in the idioms and style of that environment.
Code that comes later in a single code base will usually copy the patterns that are already established in that code base. (Assuming the starting patterns are decent, this is as it should be.)
If you add one more level of fun nesting or something like that, the labeled examples become even clearer. Since it's entirely likely that new code will copy the existing pattern, I prefer code shapes that will grow cleanly.
``` do_whatever(Keys, SomeParameter) -> [case external_lookup(Key) of {ok, V} -> do_side_effecty_thing(V, SomeParameter); {error, R} -> report_some_failure(R) end || Key <- Keys], ok. ```
or
``` [begin some_func(Key), other_func(Key) end || Key <- Keys]. ```
if only the value of other_func() needs to be in the result list.
[0] it implements a very small subset of markdown badly: emphasis (with no escape so it tends to break when you try to use multiplication signs in your comments) and indented "code" blocks
To do this you could try wrapping the list:foreach itself rather than worry about the inner function, leading to something which seems more concise and in one piece, leaving the more generic iteration to a well named function: