-module(count).
-export([count/1]).
count(L) when is_list(L) ->
do_count(L, #{});
count(_) ->
error(badarg).
do_count([], Acc) -> Acc;
do_count([H|T], #{}) -> do_count(T, #{ H => 1 });
do_count([H|T], Acc = #{ H := C }) -> do_count(T, Acc#{ H := C + 1});
do_count([H|T], Acc) -> do_count(T, Acc#{ H => 1 }).
In this example, the third clause where the map key "H" exists and has a count associated with it, will not compile. The compiler complains:
count.erl:11: variable 'H' is unbound
Why is H unbound?
This works by the way:
do_count([], Acc) -> Acc;
do_count([H|T], Acc) -> do_count(T, maps:update_with(H, fun(C) -> C + 1 end, 1, Acc)).
But it seems like the pattern match ought to work and it doesn't.
In Erlang, all the variables are bound with the '=' statement. All variables need to start with the upper case character. In other programming languages, the '=' sign is used for the assignment, but not in the case of Erlang. As stated, variables are defined with the use of the '=' statement.
Pattern matching is used for assigning values to variables and for controlling the flow of a program. Erlang is a single assignment language, which means that once a variable has been assigned a value, the value can never be changed. Pattern matching is used to match patterns with terms.
Pattern matching allows developers to easily destructure data types such as tuples and lists. As we will see in the following chapters, it is one of the foundations of recursion in Elixir and applies to other types as well, like maps and binaries.
The answer is pretty much the same as the one I recently gave here: https://stackoverflow.com/a/46268109/240949.
When you use the same variable multiple times in a pattern, as with H in this case:
do_count([H|T], Acc = #{ H := C }) -> ...
the semantics of pattern matching in Erlang say that this is as if you had written
do_count([H|T], Acc = #{ H1 := C }) when H1 =:= H -> ...
that is, they are first bound separately, then compared for equality. But a key in a map pattern needs to be known - it can't be a variable like H1, hence the error (exactly as for field size specifiers in binary patterns, in the answer I linked to).
The main difference in this question is that you have a function head with two separate arguments, and you might think that the pattern [H|T] should be matched first, binding H before the second pattern is tried, but there is no such ordering guarantee; it's just as if you had used a single argument with a tuple pattern {[H|T], #{ H := C }}.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With