Hello,
When using protocols, most complex code used in defimpl MyProtocol, for: MyStruct needs to use multiple helper functions from the MyStruct module.
So what I generally end up with is a dummy defimpl that just forwards all calls to the implementations located in MyStruct, so those implementations can just use any local function and the exported API of MyStruct can change without worrying of modifying the protocol implementation accordingly.
Some might say that it is OOP to tie implementation to the data structure, but OOP is more like tying the implementation to the state. In my case, this is just the same as a regular defimpl with one more level of indirection.
The problem is that it is tedious to write all those forwarding defimpl so I tend to just write an implementation for Any and write all the forwarding calls in the __deriving__ macro.
And finally yesterday night I just wrote a macro that takes a protocol name and the defs do block, creates the protocol along with the implementation for Any containing the forwarded calls and the actual implementation for Any (that just raises).
This is basically an interface.
So I call this:
require Ark.Interface
Ark.Interface.definterface MyProtocol do
@spec get_stuff(t, binary) :: {:ok, binary} | {:error, :not_found}
def get_stuff(t, path)
end
And I’m done, I can just @derive the protocol for some structs and implement the callbacks in the struct’s module.
The interface is an actual Elixir protocol that can still be implemented with defimpl for any type (well, except for the Any type :D).
What do you think about that ?
My main gripe with Elixir protocols is just that a defimpl embedded in a module cannot access local functions.
You have to write:
defmodule X do
defstruct [:val]
def new(n) when is_integer(n) do
%__MODULE__{val: n}
end
def wrap(n) do
"the val is #{n}"
end
defimpl Prot do
def to_s(%{val: n}), do: X.wrap(n)
end
end
instead of this:
defmodule X do
@derive Prot
defstruct [:val]
def new(n) when is_integer(n) do
%__MODULE__{val: n}
end
defp wrap(n) do # defp
"the val is #{n}"
end
@impl Prot
def to_s(%{val: n}), do: wrap(n)
end






















