Marking a protocol as implemented?

If I have some Struct on which I already defined all the required callbacks for a given Protocol, is there any way to just declare that without having to write a pile of defdelegate stubs to tell defimpl about my code?

@behaviour does not seem to have this functionality, sadly… so is there something (else) in the standard library I’ve overlooked that would do, or render unnecessary, the following macro?

defmacro implements(protocol, callbacks, opts \\ []) do
  module = __CALLER__.module
  to = Keyword.get(opts, :delegate, module)

  quote location: :keep do
    defimpl unquote(protocol), for: unquote(module) do
      unquote do
        for {name, callback} <- callbacks do
          case callback do
            arity when is_integer(arity) ->
              args = for _ <- (1..arity//1), do: Macro.unique_var(:"", __MODULE__)
              quote do
                defdelegate unquote(name)(unquote_splicing(args)), to: unquote(to)
              end

            fun when is_function(fun) ->
              raise ArgumentError, "not implemented"
          end
        end
      end
    end
  end
end
1 Like

I’m not sure I quite follow what you’re aiming for; the “usual” shorthand for implementing a protocol for a struct is to say defimpl inside the struct’s module. Example from the docs:

defmodule User do
  defstruct [:email, :name]

  defimpl Size do
    # two fields
    def size(%User{}), do: 2
  end
end

Can you show an example of how you’re using your implements macro?

1 Like

I could, probably.

defmodule LatLong do
  defstruct lat: 0, long: 0

  def inspect(%LatLong{}, opts) do
    import Inspect.Algebra
    concat(["#Coord<", to_doc([lat: lat, long: long], opts), ">"])
  end

  import ImplProto, only: [implements: 2]
  implements(Inspect, [inspect: 2])
end

We dicussed this point some time ago. Unfortunately there is no current official way to skip the indirection.

Unfortunately that’s the second example (so may be missed by people following the first), and says that for can be omitted, not that it should, so I don’t know if it’s the usual way. I’ve certainly failed to persuade people to define the implementation in the module.

Maybe Protoss could help you Protoss — protoss v1.0.0