Given `a = pattern`. `match?(^a, b)` returns false. `^a = b` returns MatchError. But `pattern = b` matches. Why?

My use case is I want to dynamically match a struct based on the struct type, and a key in the struct.

Example:

defmodule Thing do
  defstruct [:id, :name]
end

things = [
  %Thing{id: 1, name: "thing1"},
  %Thing{id: 2, name: "thing2"},
  # ..other stuff, could be non-Thing structs..
]

type = Thing
id = 2

match = %{__struct__: type, id: id}

Enum.find(things, fn thing -> match?(^match, thing) end)
# => nil

match?/2 returns false here:

iex(1)> match
%{__struct__: Thing, id: 2}
iex(2)> match?(^match, %Thing{id: 2, name: "thing2"})
false
iex(3)> %{__struct__: Thing, id: 2} = %Thing{id: 2, name: "thing2"}
%Thing{id: 2, name: "thing2"}

Or with a case statement:

case %Thing{id: 2, name: "thing2"} do
  %{__struct__: Thing, id: 2} -> :ok
  _ -> :not_ok
end
=> :ok

Now if we assign the pattern to a variable with match = %{__struct__: Thing, id: 2}, it no longer works.

case %Thing{id: 2, name: "thing2"} do
  ^match -> :ok
  _ -> :not_ok
end
=> :not_ok

I’m curious 1. why does behave this way? 2. What is the right way to achieve what I’m trying to achieve. Assuming that the list could be mixed structs.

Patterns cannot be constructed at runtime or assigned a variable. You can kinda see that reflected in the fact that match? is a macro and not simply a function. Using a pin will turn the comparison between the pinned variable and the part of the right side value to match into an eqality comparison (==) not a match by „pattern“. So your only option here would be explicitly coding matches per type and selecting only the code path at runtime.

2 Likes

^match will compare the two values for equality. Your match variable is an incomplete struct without the :name property, so not equal to your proper Thing struct with id: 2.

3 Likes

And btw, you don’t need __struct__ field to match on type of a struct. You can just do

%type{} = %SomeStruct{}
IO.inspect(type) # prints SomeStruct

and type will contain the type of a struct.

2 Likes