ankush981
ankush981

Reputation: 5417

Illegal pattern when using maps

My question is related to an existing question (and boy, was I surprised to see it was a known bug in Erlang itself!). I'm using the same count_characters example from the book, and getting the same error even though I'm on R19.

Code:

% Frequency count of characters in a string
count_characters(Str) ->
    count_characters(Str, #{}).

count_characters([H|T], #{ H => N }=X) ->
    count_characters(T, X#{ H := N+1 });
count_characters([H|T], X) ->
    count_characters(T, X#{ H => 1 });
count_characters([], X) -> X.

and the error:

1> c(lib_misc).
lib_misc.erl:40: illegal pattern
lib_misc.erl:41: variable 'N' is unbound
error

Here line 40 refers to the first clause of count-characters/2.

My questions are:

I can see the accepted answer on the linked page uses stuff like maps:update. I could do the same, but I'd first like to know why the error exists.

Upvotes: 2

Views: 257

Answers (1)

Alexey Romanov
Alexey Romanov

Reputation: 170745

The current error you see doesn't come from an Erlang bug. => is used for constructing maps, := for pattern matching (both are allowed for updating, the difference is that := only works for keys already in the map and => allows adding new keys). So you need := in the pattern:

count_characters([H|T], #{ H := N }=X) ->
    % H => N+1 is also legal here, and means the same because we know H is a key of X
    count_characters(T, X#{ H := N+1 }); 

However, after you fix this you do run into the problem: H isn't bound yet in the #{ H := N } pattern, and this isn't currently supported. This could be fixed by matching multiple argument patterns in order, so that H gets bound by [H|T]. This isn't done in R19B (at least, judging from this example) and I don't know if there are any plans to change this. It makes sense to me personally that the patterns are checked simultaneously, so I am not even sure this change would be desirable.

You can work around this by combining the first two clauses and matching X in the body instead of the head:

count_characters([H|T], X) ->
    case X of 
        #{ H := N } -> count_characters(T, X#{ H => N+1 });
        _ -> count_characters(T, X#{ H => 1 })
    end;
count_characters([], X) -> X.

Upvotes: 3

Related Questions