10 Maps
Maps are considered experimental during OTP 17 and may be subject to change.
The documentation below describes it being possible to use arbitrary expressions or variables as keys, this is NOT implemented in the current version of Erlang/OTP.
Exceptions returns badarg instead of badmap, this will change in the future releases.
10.1 Creating Maps
Constructing a new map is done by letting an expression K be associated with another expression V:
#{ K => V }
New maps may include multiple associations at construction by listing every association:
#{ K1 => V1, .., Kn => Vn }
An empty map is constructed by not associating any terms with each other:
#{}
All keys and values in the map are terms. Any expression is first evaluated and then the resulting terms are used as key and value respectively.
Keys and values are separated by the => arrow and associations are separated by ,.
Examples:
M0 = #{}, % empty map M1 = #{a => <<"hello">>}, % single association with literals M2 = #{1 => 2, b => b}, % multiple associations with literals M3 = #{k => {A,B}}, % single association with variables M4 = #{{"w", 1} => f()}. % compound key associated with an evaluated expression
where, A and B are any expressions and M0 through M4 are the resulting map terms.
If two matching keys are declared, the latter key will take precedence.
Example:
1> #{1 => a, 1 => b}. #{1 => b } 2> #{1.0 => a, 1 => b}. #{1 => b, 1.0 => a}
The order in which the expressions constructing the keys and their associated values are evaluated is not defined. The syntactic order of the key-value pairs in the construction is of no relevance, except in the above mentioned case of two matching keys.
10.2 Updating Maps
Updating a map has similar syntax as constructing it.
An expression defining the map to be updated is put in front of the expression defining the keys to be updated and their respective values.
M#{ K => V }
where M is a term of type map and K and V are any expression.
If key K does not match any existing key in the map, a new association will be created from key K to value V. If key K matches an existing key in map M its associated value will be replaced by the new value V. In both cases the evaluated map expression will return a new map.
If M is not of type map an exception of type badmap is thrown.
To only update an existing value, the following syntax is used,
M#{ K := V }
where M is an term of type map, V is an expression and K is an expression which evaluates to an existing key in M.
If key K does not match any existing keys in map M an exception of type badarg will be triggered at runtime. If a matching key K is present in map M its associated value will be replaced by the new value V and the evaluated map expression returns a new map.
If M is not of type map an exception of type badmap is thrown.
Examples:
M0 = #{}, M1 = M0#{a => 0}, M2 = M1#{a => 1, b => 2}, M3 = M2#{"function" => fun() -> f() end}, M4 = M3#{a := 2, b := 3}. % 'a' and 'b' was added in `M1` and `M2`.
where M0 is any map. It follows that M1 .. M4 are maps as well.
More Examples:
1> M = #{1 => a}. #{1 => a } 2> M#{1.0 => b}. #{1 => a, 1.0 => b}. 3> M#{1 := b}. #{1 => b} 4> M#{1.0 := b}. ** exception error: bad argument
As in construction, the order in which the key and value expressions are evaluated is not defined. The syntactic order of the key-value pairs in the update is of no relevance, except in the case where two keys match, in which case the latter value is used.
10.3 Maps in Patterns
Matching of key-value associations from maps is done in the following way:
#{ K := V } = M
where M is any map. The key K has to be an expression with bound variables or a literals, and V can be any pattern with either bound or unbound variables.
If the variable V is unbound, it will be bound to the value associated with the key K, which has to exist in the map M. If the variable V is bound, it has to match the value associated with K in M.
Example:
1> M = #{"tuple" => {1,2}}. #{"tuple" => {1,2}} 2> #{"tuple" := {1,B}} = M. #{"tuple" => {1,2}} 3> B. 2.
This will bind variable B to integer 2.
Similarly, multiple values from the map may be matched:
#{ K1 := V1, .., Kn := Vn } = M
where keys K1 .. Kn are any expressions with literals or bound variables. If all keys exist in map M all variables in V1 .. Vn will be matched to the associated values of their respective keys.
If the matching conditions are not met, the match will fail, either with
- a badmatch exception, if used in the context of the matching operator as in the example,
- or resulting in the next clause being tested in function heads and case expressions.
Matching in maps only allows for := as delimiters of associations. The order in which keys are declared in matching has no relevance.
Duplicate keys are allowed in matching and will match each pattern associated to the keys.
#{ K := V1, K := V2 } = M
Matching an expression against an empty map literal will match its type but no variables will be bound:
#{} = Expr
This expression will match if the expression Expr is of type map, otherwise it will fail with an exception badmatch.
Matching syntax: Example with literals in function heads
Matching of literals as keys are allowed in function heads.
%% only start if not_started handle_call(start, From, #{ state := not_started } = S) -> ... {reply, ok, S#{ state := start }}; %% only change if started handle_call(change, From, #{ state := start } = S) -> ... {reply, ok, S#{ state := changed }};
10.4 Maps in Guards
Maps are allowed in guards as long as all sub-expressions are valid guard expressions.
Two guard BIFs handles maps: