From 2b42af9aab024db55f57aa5d70751712074f2bab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 18 Mar 2014 15:26:57 +0100 Subject: doc: Document Maps datatype in reference manual --- system/doc/reference_manual/data_types.xml | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'system') diff --git a/system/doc/reference_manual/data_types.xml b/system/doc/reference_manual/data_types.xml index 8c690d6b86..0031664dfb 100644 --- a/system/doc/reference_manual/data_types.xml +++ b/system/doc/reference_manual/data_types.xml @@ -189,6 +189,38 @@ adam 0 +
+ Map +

Compound data type with a variable number of key-value associations:

+
+#{Key1=>Value1,...,KeyN=>ValueN}
+

Each key-value association in the map is called an + association pair. The key and value parts of the pair are + called elements. The number of association pairs is said to be + the size of the map.

+

There exists a number of BIFs to manipulate maps.

+

Examples:

+
+1> M1 = #{name=>adam,age=>24,date=>{july,29}}.
+#{age => 24,date => {july,29},name => adam}
+2> maps:get(name,M1).
+adam
+3> maps:get(date,M1).
+{july,29}
+4> M2 = maps:update(age,25,M1).
+#{age => 25,date => {july,29},name => adam}
+5> map_size(M).
+3
+6> map_size(#{}).
+0
+

A collection of maps processing functions can be found in + the STDLIB module maps.

+

Read more about Maps.

+ +

Maps are considered experimental during OTP 17.

+
+
+
List

Compound data type with a variable number of terms.

-- cgit v1.2.3 From f8bd9be45de37fa20bb82721c8ba2fc67b8a7cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 18 Mar 2014 15:31:41 +0100 Subject: doc: Clearify language of user-defined attributes --- system/doc/reference_manual/modules.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'system') diff --git a/system/doc/reference_manual/modules.xml b/system/doc/reference_manual/modules.xml index 9e5f4de385..77a8b1dc95 100644 --- a/system/doc/reference_manual/modules.xml +++ b/system/doc/reference_manual/modules.xml @@ -53,10 +53,10 @@ fact(0) -> % |
 -Tag(Value).

Tag must be an atom, while Value must be a literal - term. As a convenience in user-defined attributes, the literal term - Value the syntax Name/Arity - (where Name is an atom and Arity a positive integer) - will be translated to {Name,Arity}.

+ term. As a convenience in user-defined attributes, if the literal term + Value has the syntax Name/Arity + (where Name is an atom and Arity a positive integer), + the term Name/Arity will be translated to {Name,Arity}.

Any module attribute can be specified. The attributes are stored in the compiled code and can be retrieved by calling -- cgit v1.2.3 From acea637e9c0cbdb460419eaba505ca02283ecd02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 18 Mar 2014 16:14:54 +0100 Subject: doc: Descripe Maps type syntax --- system/doc/reference_manual/typespec.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'system') diff --git a/system/doc/reference_manual/typespec.xml b/system/doc/reference_manual/typespec.xml index 71aec732cf..cc35c6eb21 100644 --- a/system/doc/reference_manual/typespec.xml +++ b/system/doc/reference_manual/typespec.xml @@ -100,6 +100,7 @@ | Fun | Integer | List + | Map | Tuple | Union | UserDefined %% described in Section 6.3 @@ -126,10 +127,17 @@ | nonempty_improper_list(Type1, Type2) %% Type1 and Type2 as above | nonempty_list(Type) %% Proper non-empty list + Map :: map() %% stands for a map of any size + | #{} %% stands for a map of any size + | #{PairList} + Tuple :: tuple() %% stands for a tuple of any size | {} | {TList} + PairList :: Type => Type + | Type => Type, PairList + TList :: Type | Type, TList @@ -275,6 +283,10 @@ Records have been extended to possibly contain type information. This is described in the sub-section "Type information in record declarations" below.

+ +

Map types, both map() and #{ ... }, are considered experimental during OTP 17.

+

No type information of maps pairs, only the containing map types, are used by Dialyzer in OTP 17.

+
-- cgit v1.2.3 From f140825e037714166501615864d5e5efd4557bd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 24 Mar 2014 16:19:16 +0100 Subject: doc: Add maps to reference manual --- system/doc/reference_manual/maps.xml | 274 ++++++++++++++++++++++++++++++++ system/doc/reference_manual/part.xml | 1 + system/doc/reference_manual/xmlfiles.mk | 1 + 3 files changed, 276 insertions(+) create mode 100644 system/doc/reference_manual/maps.xml (limited to 'system') diff --git a/system/doc/reference_manual/maps.xml b/system/doc/reference_manual/maps.xml new file mode 100644 index 0000000000..78808ce4a2 --- /dev/null +++ b/system/doc/reference_manual/maps.xml @@ -0,0 +1,274 @@ + + + + +
+ + 2014 + Ericsson AB. All Rights Reserved. + + + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + + Maps + + + + + maps.xml +
+ + +

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.

+
+ +
+ 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. +

+
+ +
+ 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. +

+
+ +
+ 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 }}; +
+
+
+ Maps in Guards +

+ Maps are allowed in guards as long as all sub-expressions are valid guard expressions. +

+

+ Two guard BIFs handles maps: +

+ + + is_map/1 + + + map_size/1 + + +
+
diff --git a/system/doc/reference_manual/part.xml b/system/doc/reference_manual/part.xml index b4f114c268..09f7a41d0a 100644 --- a/system/doc/reference_manual/part.xml +++ b/system/doc/reference_manual/part.xml @@ -35,6 +35,7 @@ + diff --git a/system/doc/reference_manual/xmlfiles.mk b/system/doc/reference_manual/xmlfiles.mk index 6886c8c7cf..181e6f8042 100644 --- a/system/doc/reference_manual/xmlfiles.mk +++ b/system/doc/reference_manual/xmlfiles.mk @@ -24,6 +24,7 @@ REF_MAN_CHAPTER_FILES = \ functions.xml \ expressions.xml \ macros.xml \ + maps.xml \ records.xml \ errors.xml \ processes.xml \ -- cgit v1.2.3 From 59ddad4368adfe8992f3bb5c7a1c9a06204ca7fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Thu, 27 Mar 2014 14:34:55 +0100 Subject: doc: Mention map expressions and map guards --- system/doc/reference_manual/expressions.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'system') diff --git a/system/doc/reference_manual/expressions.xml b/system/doc/reference_manual/expressions.xml index e9de3e006e..37208710fe 100644 --- a/system/doc/reference_manual/expressions.xml +++ b/system/doc/reference_manual/expressions.xml @@ -56,7 +56,7 @@ Expr1 + Expr2 Terms

The simplest form of expression is a term, that is an integer, - float, atom, string, list or tuple. + float, atom, string, list, map or tuple. The return value is the term itself.

@@ -1347,6 +1347,9 @@ end is_list/1 + + is_map/1 + is_number/1 @@ -1397,6 +1400,9 @@ end length(List) + + map_size(Map) + node() -- cgit v1.2.3 From fbb05239aeccc400aa4e4a359a3fb81ef555e441 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 18 Mar 2014 14:59:50 +0100 Subject: doc: Add Maps example for sequential programming --- system/doc/getting_started/seq_prog.xml | 123 ++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) (limited to 'system') diff --git a/system/doc/getting_started/seq_prog.xml b/system/doc/getting_started/seq_prog.xml index 3830a34e5a..fd49102263 100644 --- a/system/doc/getting_started/seq_prog.xml +++ b/system/doc/getting_started/seq_prog.xml @@ -418,6 +418,129 @@ list_length([First | Rest]) -> "abc" +
+ Maps +

Maps are a set of key to value associations. These associations + are encapsulated with "#{" and "}". To create an association from + "key" to value 42, we write:

+ +> #{ "key" => 42 }. +#{"key" => 42} +

We will jump straight into the deep end with an example using some + interesting features.

+

The following example shows how we calculate alpha blending using + maps to reference color and alpha channels:

+ +-module(color). + +-export([new/4, blend/2]). + +-define(is_channel(V), (is_float(V) andalso V >= 0.0 andalso V =< 1.0)). + +new(R,G,B,A) when ?is_channel(R), ?is_channel(G), + ?is_channel(B), ?is_channel(A) -> + #{red => R, green => G, blue => B, alpha => A}. + +blend(Src,Dst) -> + blend(Src,Dst,alpha(Src,Dst)). + +blend(Src,Dst,Alpha) when Alpha > 0.0 -> + Dst#{ + red := red(Src,Dst) / Alpha, + green := green(Src,Dst) / Alpha, + blue := blue(Src,Dst) / Alpha, + alpha := Alpha + }; +blend(_,Dst,_) -> + Dst#{ + red := 0.0, + green := 0.0, + blue := 0.0, + alpha := 0.0 + }. + +alpha(#{alpha := SA}, #{alpha := DA}) -> + SA + DA*(1.0 - SA). + +red(#{red := SV, alpha := SA}, #{red := DV, alpha := DA}) -> + SV*SA + DV*DA*(1.0 - SA). +green(#{green := SV, alpha := SA}, #{green := DV, alpha := DA}) -> + SV*SA + DV*DA*(1.0 - SA). +blue(#{blue := SV, alpha := SA}, #{blue := DV, alpha := DA}) -> + SV*SA + DV*DA*(1.0 - SA). +

Compile (file color.erl) and test:

+
+> c(color).
+{ok,color}
+> C1 = color:new(0.3,0.4,0.5,1.0).
+#{alpha => 1.0,blue => 0.5,green => 0.4,red => 0.3}
+> C2 = color:new(1.0,0.8,0.1,0.3).
+#{alpha => 0.3,blue => 0.1,green => 0.8,red => 1.0}
+> color:blend(C1,C2).
+#{alpha => 1.0,blue => 0.5,green => 0.4,red => 0.3}
+> color:blend(C2,C1).
+#{alpha => 1.0,blue => 0.38,green => 0.52,red => 0.51}
+
+

This example warrant some explanation:

+ +-define(is_channel(V), (is_float(V) andalso V >= 0.0 andalso V =< 1.0)). +

+ First we define a macro is_channel to help with our guard tests. + This is only here for convenience and to reduce syntax cluttering. + + You can read more about Macros + in the Erlang Reference Manual. +

+ +new(R,G,B,A) when ?is_channel(R), ?is_channel(G), + ?is_channel(B), ?is_channel(A) -> + #{red => R, green => G, blue => B, alpha => A}. +

+ The function new/4 creates a new map term with and lets the keys + red, green, blue and alpha be associated + with an initial value. In this case we only allow for float + values between and including 0.0 and 1.0 as ensured by the ?is_channel/1 macro + for each argument. Only the => operator is allowed when creating a new map. +

+

+ By calling blend/2 on any color term created by new/4 we can calculate + the resulting color as determined by the two maps terms. +

+

+ The first thing blend/2 does is to calculate the resulting alpha channel. +

+ +alpha(#{alpha := SA}, #{alpha := DA}) -> + SA + DA*(1.0 - SA). +

+ We fetch the value associated with key alpha for both arguments using + the := operator. Any other keys + in the map are ignored, only the key alpha is required and checked for. +

+

This is also the case for functions red/2, blue/2 and green/2.

+ +red(#{red := SV, alpha := SA}, #{red := DV, alpha := DA}) -> + SV*SA + DV*DA*(1.0 - SA). +

+ The difference here is that we check for two keys in each map argument. The other keys + are ignored. +

+

+ Finally we return the resulting color in blend/3. +

+ +blend(Src,Dst,Alpha) when Alpha > 0.0 -> + Dst#{ + red := red(Src,Dst) / Alpha, + green := green(Src,Dst) / Alpha, + blue := blue(Src,Dst) / Alpha, + alpha := Alpha + }; +

+ We update the Dst map with new channel values. The syntax for updating an existing key with a new value is done with := operator. +

+
+
Standard Modules and Manual Pages

Erlang has a lot of standard modules to help you do things. For -- cgit v1.2.3