Pattern matching a struct on a map via a pinned variable

defmodule Foo do
  defstruct [:foo]
end

%{foo: "foo"} = %Foo{foo: "foo"} # Works

foo = %{foo: "foo"}
^foo = %Foo{foo: "foo"} # Fails with "** (MatchError) no match of right hand side value: %Foo{foo: "foo"}"

Is this expected?

The doc says:

iex> ^x = 2

Because we have pinned x when it was bound to the value of 1 , it is equivalent to the following:

iex> 1 = 2

I guess this is not the same thing when a map is on the left side.

It is the same thing, but you’re mixing up values and patterns. ^ is not a pattern substitution operator. It binds a value inside a pattern and always checks for exact equality. It happens in the case of simple integers that the value 1 and the pattern 1 are equal. In case of lists however [1 | rest] is a valid pattern but if you had:

iex(1)> rest = [2,3]
[2, 3]
iex(2)> list = [1 | rest]
[1, 2, 3]
iex(3)> ^list = [1,2,3,4]
** (MatchError) no match of right hand side value: [1, 2, 3, 4]
iex(3)> [1 | rest] = [1,2,3,4]
[1, 2, 3, 4]

The same thing happens with maps as you observe. When you do ^foo = it’s going to check that foo is exactly equal to the relevant part of the right hand side, it isn’t going to convert foo's value into a pattern.

5 Likes

OK, I understand, thank you.

I thought I could use ^ to “reuse” a pattern like this:

^pattern = value
^pattern = other_value
...