Elixir: When match? is always true

This is a quick post to highlight what we found is an easy mistake to make when encountering the match? macro. Which from the documentation is:

“A convenience macro that checks if the right side (an expression) matches the left side (a pattern).”

The important piece of information in that statement is “the left hand side (a pattern)”.

How does match? work?

Let’s look at match? with some number literals:

iex> match?(1, 1)
true
iex> match?(1, 2)
false

So far so good, how about match? with a pattern in it:

iex> match?(%{key: "value"}, %{key: "value"})
true
iex> match?(%{key: "other"}, %{key: "value"})
false

What if we wanted to introduce some variables to make the code more readable. Let’s introduce a contrived right_map variable.

iex> right_map = %{key: "value"}
%{key: "value"}
iex> match?(%{key: "value"}, right_map)
true
iex> match?(%{key: "other"}, right_map)
false

Everything still looks in order, now how about a left_map_value and a left_map_other variable.

iex> right_map = %{key: "value"}
%{key: "value"}
iex> left_map_value = %{key: "value"}
%{key: "value"}
iex> left_map_other = %{key: "other"}
%{key: "other"}
iex> match?(left_map_value, right_map)
true
iex> match?(left_map_other, right_map)
true

Notice that both calls to match? now return true, that is not what we want at all. The implementation of the match? macro sheds some light on what is going on:

defmacro match?(pattern, expr) do
quote do
case unquote(expr) do
unquote(pattern) ->
true
_ ->
false
end
end
end

I won’t go into macros in this post, but if we use the example above, this is equivalent to the expression:

case right_map do
left_map_other ->
true
_ ->
false
end

Remember that variables can be rebound, which is exactly what is happening here. Essentially left_map_other is getting bound to the value of right_map, and then true is returned. It makes no difference what the value of left_map_other is, the expression above will always be true. We can prove this by returning the value of left_map_other:

iex> case right_map do                                                
...> left_map_other -> left_map_other
...> _ -> true
...> end
%{key: "value"}
iex> left_map_other
%{key: "other"}

In the scope of the first case clause left_map_other is bound to the value %{key: "value"}, outside of that it is bound to the original value %{key: "other"}. This is simple enough to fix now that we now what is going on, we just need to pin the left_map_other so that the value of left_map_other is the pattern.

iex> match?(^left_map_value, right_map)
true
iex> match?(^left_map_other, right_map)
false

When is match? useful?

I have found match? handy when you care about only whether something matches, not binding variables, as we have seen variables bound inside match? will not be available to the caller. The most common cases include working with Enumerables and during testing. The great thing about match? is that, as we have seen, the left hand side can be anything that is valid in a case clause. This means that guard clauses are valid, the documentation has an example of this (i've slightly modified it):

iex> list = [a: 1, b: 2, c: 3]
[a: 1, b: 2, c: 3]
iex> Enum.filter(list, &match?({:a, x} when x < 2, &1))
[a: 1]

Wrapping Up

So match? actually does exactly what it says on the tin. It is just important to remember what is really going on when refactoring and introducing variables. It is a macro that I find comes in quite handy.