Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When pattern matching maps in Erlang, why is this variable unbound?

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

like image 305
Jade Allen Avatar asked Sep 23 '17 07:09

Jade Allen


People also ask

Which symbol is used to declare unbound values in erlang?

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.

What is pattern matching in Erlang?

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.

What is pattern matching in Elixir?

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.


1 Answers

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

like image 181
RichardC Avatar answered Nov 15 '22 11:11

RichardC