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.

1 Like

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.

3 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