Many years ago, this would’ve been a tweet, but I don’t really use that platform anymore, so I’ll just post it here.
It is kind of related to these articles that I wrote years ago:
The idea is that, in Erlang, you do not need to have generators on your list comprehensions…
1> WeirdEquals = fun(A, B) -> [equal] =:= [equal || A =:= B] end.
#Fun<erl_eval.43.65746770>
2> WeirdEquals(1,1).
true
3> WeirdEquals(1,2).
false
That, on its own, can be used to write else-less ifs: Instead of…
if A < 0 -> io:format("Hey, ~p is a negative number!", [A]) end
…which will crash if A >= 0
, you write…
[io:format("Hey, ~p is a negative number!", [A]) || A < 0]
Don’t do this at home, kids
Anyway… Today I found another strange (but clever) thing you can do with List Comprehensions (or with comprehensions in general). You can put a filter before the first generator. For instance, let say you have this problem…
4> Values = fun(MaybeList) -> [V || {K, V} <- MaybeList] end.
#Fun<erl_eval.44.65746770>
5> Values([]).
[]
6> Values([a]).
[]
7> Values([{k1, v1}, {k2, v2}]).
[v1,v2]
8> Values(not_a_list).
** exception error: bad generator not_a_list
And for whatever reason, you want the function to return []
even if what’s given to it is not a list. Of course, you can define your function as…
1> Values = fun(MaybeList) when not is_list(MaybeList) -> [];
(List) -> [V || {K, V} <- List]
end.
But… what’s the fun in that?!! Specially when you can also define the function as…
3> Values = fun(MaybeList) -> [V || is_list(MaybeList), {K, V} <- MaybeList] end.
#Fun<erl_eval.42.113135111>
4> Values([]).
[]
5> Values([a]).
[]
6> Values([{k1, v1}, {k2, v2}]).
[v1,v2]
7> Values(not_a_list).
[]
I’m not sure if this is a practice that should be encouraged, but it may be a clever tool to keep around, just in case so I thought I would share it