[erlang-questions] Request feedback on example project
Richard A. O'Keefe
ok@REDACTED
Thu Feb 4 22:30:28 CET 2016
On 5/02/16 9:09 am, Siraaj Khandkar wrote:
> (1) surprising filter semantics:
>
> 1> L = [{foo, a}, {bar, b}].
> [{foo,a},{bar,b}]
> 2>
> 2> [X || {foo, X} <-L].
> [a]
> 3>
This is how list comprehensions are supposed to work.
What's surprising about that?
> While lists:map/2 crashes, as expected:
3> lists:map(fun ({foo, X}) -> X end, L).
Yes, but that is not a translation of the list comprehension
and the list comprehension is not a translation of this.
They are different, and that is one important reason to
prefer the list comprehension. To get the effect of the
list comprehension -- which is a commonly desired effect --
you would have to write
lists:flatmap(fun ({foo,X}) -> [X]
; (_) -> []
end, L).
which is a bit confusing because there isn't any useful
connection between flatmap and flatten.
To translate your map call as a list comprehension,
F = fun ({foo,X}) -> X end, [F(X) || X <- L].
> There're certainly ways around that, such as:
Why even think about "ways around that"? List comprehensions
and calls to map are both doing their intended, documented,
and different jobs.
>
> 4> [begin {foo, Foo} = X, Foo end || X <- L].
> ** exception error: no match of right hand side value {bar,b}
> 5>
> 5> [(fun ({foo, Foo}) -> Foo end)(X) || X <- L].
> ** exception error: no function clause matching
> erl_eval:'-inside-an-interpreted-fun-'({bar,b})
> 6>
>
> But all that feels like extra gymnastics to just use list
> comprehensions for the sake of using list comprehensions.
No, it's extra gymnastics in the service of a straw man argument.
In real code, I *want* the filtering behaviour of list comprehensions,
so I use them instead of switching over to bulky calls to flatmap/2.
> (2) Explicitly-declared intention. When you explicitly call "filter"
> and "map" - I have an immediate high-level idea of where you're going
> with it, but list comprehensions tempt one to conflate things, so it
> is harder to understand the intention (there're some gnarly ones out
> in the wild...).
It is possible to write unreadable tangles of code using filter and map
as well.
One thing I would mention is the linguistic concept of "topicalisation",
the way that people can shuffle different parts of a sentence to the
beginning to highlight it. In
[hypot(X, Y) || {X,Y} <- zip(Xs, Ys)]
the topic is hypot(X, Y): we are invited to focus on the *result*.
In
map(zip(Xs,Ys), fun ({X,Y}) -> hypot(X, Y) end)
the topic is zip(Xs, Ys): we are invited to focus on the *inputs*, not
on what happens to them.
Looking at it from this viewpoint, it is immediately obvious that
*SOME* expressions will be more intention-revealing when written
with the inputs first so that map or flatmap + filter is a good fit
*SOME* expressions will be more intention-revealing when written
with the results first.
It's just like the way Haskell has *both*
let Pattern = Expression in Result
and
Result where Pattern = Expression
The fact that something is a call to map or a call to filter is NOT
intention-revealing as such. All that tells me is the what, not the
why, and the why is the intention.
It is good that Erlang has *both* list comprehensions *and* higher-
order functions. Do not blame either for not being identical to the
other.
More information about the erlang-questions
mailing list