Why Map comprehensions can't run without erlang/otp 25

/Users/leeyi/project/imboy.pub/imboy >
erl
Erlang/OTP 25 [erts-13.2.2.4] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns] [dtrace]

Eshell V13.2.2.4  (abort with ^G)
1> Weather = #{toronto => rain, montreal => storms, london => fog, paris => sun, boston => fog, vancouver => snow}.
#{boston => fog,london => fog,montreal => storms,paris => sun,
  toronto => rain,vancouver => snow}
2> 
2> 
2> Weather.
#{boston => fog,london => fog,montreal => storms,paris => sun,
  toronto => rain,vancouver => snow}
3> FoggyPlaces = [X || X := fog <- Weather].
* 1:23: syntax error before: ':='
3> [X || X => fog <- Weather].
* 1:9: syntax error before: '=>'

The comprehension which you used would work with a list:

WeatherList = [{toronto, rain}, {montreal, storms}, {london, fog}, {paris, sun}, {boston, fog}, {vancouver, snow}].
7> [X || X={_, fog} ← WeatherList].
[{london,fog},{boston,fog}]

But as your data is in a map, you must use map comprehension syntax, like this:

#{City => fog || City := fog ← Weather}.

I think map comprehensions were introduced with 26:

Correct :+1:

Yup, the article you took this example says

The same kind of stuff can be done with map comprehensions, once they’re made available

And map comprehensions were only introduced in OTP-26 (while maps were introduced much earlier - in OTP-17)

Thank you for reminding me!
I didn’t read carefully enough to notice “once they’re made available.”

Available in Erlang/OTP 26

erl
Erlang/OTP 26 [erts-14.2.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit] [dtrace]

Eshell V14.2.1 (press Ctrl+G to abort, type help(). for help)
1> Weather = #{toronto => rain, montreal => storms, london => fog, paris => sun, boston => fog, vancouver => snow}.
#{toronto => rain,montreal => storms,london => fog,
  paris => sun,boston => fog,vancouver => snow}
2> FoggyPlaces = [X || X := fog <- Weather].
[london,boston]
1 Like

That is a list comprehension using a map generator :sweat_smile:

That said, all of this should work (in OTP 26):

1> [ {X} || X <- [1, 2, 3] ].
[{1}, {2}, {3}]
2> [ {X} || <<X>> <= <<4, 5, 6>> ].
[{4}, {5}, {6}]
3> [ {X, Y} || X := Y #{7 => 8, 9 => 10, 11 => 12} ].
[{7, 8}, {9, 10}, {11, 12}]

4> << <<(X*X)>> || X <- [1, 2, 3] >>.
<<1, 4, 9>>
5> << <<(X*X)>> || <<X>> <- <<4, 5, 6>> >>.
<<16, 25, 36>>.
6> << <<(X*Y)>> || X := Y <- #{7 => 8, 9 => 10, 11 => 12} >>.
<<56, 90, 132>>

7> #{ X => foo || X <- [1, 2, 3] }.
#{1 => foo, 2 => foo, 3 => foo}
8> #{ X => bar || <<X>> <= <<4, 5, 6>> }.
#{4 => bar, 5 => bar, 6 => bar}
9> #{ Y => X || X := Y <- #{7 => 8, 9 => 10, 11 => 12} }.
#{8 => 7, 10 => 9, 12 => 11}

List and especially binary comprehensions with a map generator are of limited use, however, since there is no defined order. For example, the list resulting from [{X, Y} || X := Y <- #{1 => 2, 3 => 4}] could be either [{1, 2}, {3, 4}] or [{3, 4}, {1, 2}].

5 Likes

And this is the reason why in OTP-26 they added “ordered map iterators” and map generators support map iterators as input:

> Map => #{1 => a, 2 => b}.
> [{K, V} || K := V <- maps:iterator(Map, ordered)].
[{1, a}, {2, b}]
> [{K, V} || K := V <- maps:iterator(Map, reversed)].
[{2, b}, {1, a}]
3 Likes