defmodule Ark.Interface do defmacro definterface(proto, [{:do, block}]) do {:__block__, _, top_level} = block defs = Enum.filter( top_level, &(is_tuple(&1) and tuple_size(&1) == 3 and :def == elem(&1, 0)) ) # Generate implementation of the protocol that will throw defs_for_any_itself = Enum.map(defs, fn {:def, _meta, [{function, _meta2, args}]} -> args = Enum.map(args, fn _ -> {:_, [], nil} end) quote do def unquote(function)(unquote_splicing(args)) do raise "#{inspect(unquote(proto))} must be derived" end end end) [ # Define the protocol quote location: :keep do defprotocol unquote(proto) do unquote(block) end end, # Define the implemementation for Any, containing the deriving macro quote location: :keep do defimpl unquote(proto), for: Any do defmacro __deriving__(_module, _struct, _options) do proto = unquote(proto) quote do require Ark.Interface Ark.Interface.auto_impl(unquote(proto)) end end unquote(defs_for_any_itself) end end ] end defmacro auto_impl(proto) do module = __CALLER__.module quote location: :keep, bind_quoted: [ proto: proto, module: module ] do defs = proto.__protocol__(:functions) defimpl proto do Enum.each(defs, fn {function, arity} when arity > 0 -> args = Enum.map(1..arity//1, fn i -> Macro.var(:"arg_#{i}", :auto_defimpl) end) def unquote(function)(unquote_splicing(args)) do unquote(module).unquote(function)(unquote_splicing(args)) end end) end end end end