Lists, Maps and Commas
This will be a super-quick post about something that came up at least twice in the last few months within the Erlang community. It’s related to the syntax of maps within a list.


How Many Maps?
Quick question: how many maps do we have in the following list?
[#{a => 1},
#{b => 2}
#{c => 3}].- 1
- 2
- 3
- That doesn’t even compile!
People following the erlang-questions mailing list might know already, but the correct answer is not 3, nor a compile error. It’s 2.
Why not 3?
Well, there is a missing comma in the second row.
But why does it compile, then?
Because what’s written there is a perfectly valid expression, of course. And it matches the syntax used for records, as it’s pointed out in this thread:
-module(sample).
-record (a, {field1, field2}).
-export([bug/0]).
bug() ->
[
#a{field1 = 1, field2 = "foof"} %% COMMA IS MISSING
#a{field1 = 2},
#a{field1 = 3}
].
that function on the shell returns a list with 2 elements, as expected…
1> rr(sample).
[a]
2> sample:bug().
[#a{field1 = 2,field2 = "foof"},
#a{field1 = 3,field2 = undefined}]
3>
What’s going on here?
What these expressions with the missing commas are expressing is understandable. In our example with maps what we are basically doing is:
[#{a => 1}, #{b => 2}#{c => 3}].It looks weird but, if we would’ve used a variable there…
B = #{b => 2},
[#{a => 1}, B#{c => 3}].On the other hand, you can’t just put anything on the left of the # sign there, check this out…
3> maps:new() #{b => 2}.
* 1: syntax error before: '#'In that case, you must use parentheses for this to work…
3> (maps:new())#{b => 2}.
#{b => 2}So, even when the original expression semantics are understandable, are they useful? Many of us will prefer the compiler/parser to warn us about this or straight up reject the code, but as Jesper L. Andersen points out…
In short, regarding this as an invalid expression is to a certain extent
possible, and certainly desirable. But we run into subtle problems when we
want to reject it too, which is what complicates matters.
On the other hand, Dmytro had a great idea: Elvis can check for this. That’s why I opened issue #440 (link below), if you feel like contributing ;)
Bonus Track
While researching for this article I made a couple of extra tests on the console, just to see what would happen if… Check them out:
4> A = #{b => 3} #{b => 4}.
#{b => 4}
5> A.
#{b => 4}
6> (A = #{b => 3}) #{b => 4}.
** exception error: no match of right hand side value #{b => 3}
7> f(A).
ok
8> B = (A = #{b => 3}) #{b := 4}.
#{b => 4}
9> A.
#{b => 3}
10> B.
#{b => 4}So, in case you were wondering, you can’t use the wacky syntax to do some strange pattern matching. You still have to use parentheses for that.