Guards or pattern matching for protocol implementations

Is it possible to write a function definition such that it only takes an argument that implements a given protocol? The best I’ve found is the assert_impl!\2 function, but that can’t be used in a guard. I’ve also considered the fact that protocols define a type, but I’m at a loss as to how to match against those, either.

I’m not really sure what you are trying to do. If you want to create a guard for protocol function you can write is like that:

defprotocol Size do
  @doc "Calculates the size (and not the length!) of a data structure"
  def size(data)

  @definitions Module.definitions_in(__MODULE__)
  defguard defines?(name, arity) when {name, arity} in @definitions
end

require Size
Size.defines?(:size, 0) # false
Size.defines?(:size, 1) # true

If you want to check if module implements Protocol you would need to create a new file as code for it can’t be placed in same file as Protocol. With such example code:

defprotocol Size do
  @doc "Calculates the size (and not the length!) of a data structure"
  def size(data)
end

defimpl Size, for: Tuple do
  def size(tuple), do: tuple_size(tuple)
end

we can write a guard like:

defmodule Example do
  path = :code.lib_dir(:example, :ebin)
  @modules Protocol.extract_impls(Size, [path])

  defguard implements_size?(module) when module in @modules
end

Example.implements_size?(Map) # false
Example.implements_size?(Tuple) # true

Note: In :code.lib_dir/2 as a first argument you need to give an application name which implements specific Protocol.

3 Likes