Search code examples
pattern-matchingelixirprotocols

Elixir pattern match protocol implementor


You can pattern match specific structs in your methods to prevent some leakage of type bugs. E.g. def f(z_struct = %Z{}) do ....

Can you do something similar if you don't know the type of struct, say, if you want a struct of a module that implements protocol P.?

defprotocol P do
  # ...
end

defmodule A do
  defstruct :a
end
defimpl P, for: A do
  # ...
end

defmodule B do
  defstruct :b
end
defimpl P, for: B do
  # ...
end

defmodule SomeMod do
  def m(p) do
    # ...
  end
end

I.e. method SomeMod.m/1 should accept only an %A{} or a %B{} as parameter. If module C implements P later, m/1 should accept C without code changes.

Can we do this elixir?


If the above doesnt explain well, think of it like accepting an interface in Java, where P is the interface and A and B are the classes that implement P.


Solution

  • That is impossible with guards / pattern matching in function clauses. The only way to assert this would be to use a reflection Kernel.impl_for/1:

    defmodule SomeMod do
      def m(p) do
        unless P.impl_for(p), do: raise MatchError, term: p
        # ...
      end
    end