Just to follow up, I’ll give here the same example for this I gave in Slack.
We can define a protocol for our use case - e.g. a repository of users:
defprotocol UserRepository do
def get(database, id)
end
And then we can define two implementations - one based on a real database and one based on a simple in-memory ETS store:
defmodule Database do
defstruct [:ecto_repo]
def new(repo), do: %__MODULE__{ecto_repo: repo}
end
defmodule EtsDatabse do
defstruct [:table]
def new(), do: %__MODULE__{table: :ets.new(__MODULE__, [:set, :public])}
end
We can then implement the protocol for those data structures
defimpl UserRepository, for: Database do
def get(%{ecto_repo: repo}, id) do
repo.get(User, id)
end
end
defimpl UserRepository, for; EtsDatabase do
def get(%{table: table}, id) do
# assuming {{User, id}, struct} tuples in ets table
:ets.lookup_element(table, {User, id}, 2)
end
end
That’s a simple sketch of a protocol-based dependency injection.