illegal pattern in map of Erlang

@EWit, Felipe Mafra:

maps does just what it is supposed to do; what's missing here is the reduce part:

count(Str) -> M = count_chars(Str, maps:new()), % maps part, bad naming
    L = maps:to_list(M),                        % to be able to sum
    N = [X || {_,X} <- L],                      % strip the numbers
    lists:sum(N).                               % sum them up

count_chars([H|T], Map) when is_map(Map)->
    N = maps:get(H, Map, 0),
    count_chars(T, maps:put(H, N + 1, Map));
count_chars([], Map) -> Map.

Quoted from OTP 17.0 Release Notes:

OTP-11616 == erts stdlib hipe dialyzer compiler typer ==

    EEP43: New data type - Maps

    With Maps you may for instance:

    -- M0 = #{ a => 1, b => 2}, % create associations

    -- M1 = M0#{ a := 10 }, % update values

    -- M2 = M1#{ "hi" => "hello"}, % add new associations

    -- #{ "hi" := V1, a := V2, b := V3} = M2. % match keys with
    values

    For information on how to use Maps please see the Reference
    Manual.

    The current implementation is without the following features:

    -- No variable keys

    -- No single value access

    -- No map comprehensions

    Note that Maps is experimental during OTP 17.0.

Currently you can use maps module to implement count_characters:

count_characters(Str) ->
    count_characters(Str, #{}).

count_characters([H|T], X) -> 
    count_characters(T, maps:put(H, maps:get(H, X, 0) + 1, X));
count_characters([], X) ->
    X.

The answers from IRC (#erlang@freenode):

  1. variables as keys in matches are not supported yet (release 17.0)
  2. A more general issue affects matching arguments of a function: line 7's H is matched 2 times; or once and used to match N then. (This issue also appears with binaries)

This should be solved in the coming releases.

As of release 17 this works:

-module(count_chars).
-export([count_characters/1]).

count_characters(Str) ->
        count_characters(Str, #{}).

%% maps module functions cannot be used as guards (release 17)
%% or you'll get "illegal guard expression" error
count_characters([H|T], X) ->
    case maps:is_key(H,X) of
        false -> count_characters(T, maps:put(H,1,X));
        true  -> Count = maps:get(H,X),
                         count_characters(T, maps:update(H,Count+1,X))
    end;
count_characters([], X) ->
        X.

Here is another version (only tested on 18) that is slightly more similar to the one in the book:

-module(count_chars).
-export([count_characters/1]).

count_characters(Str) ->
        count_characters(Str, #{}).

count_characters([H|T], X) ->
    case maps:is_key(H,X) of
        false -> count_characters(T, X#{ H => 1 });
        true  -> #{ H := Count } = X,
                 count_characters(T, X#{ H := Count+1 })
    end;
count_characters([], X) ->
        X.

When you want to match a map, you need like this:

#{key1 := Pattern1, key2 := Pattern2, ...} = VarContainingAMap.

you can read the document: https://joearms.github.io/published/2014-02-01-big-changes-to-erlang.html

Tags:

Erlang

Map