Elixir Behaviours
in combination with Elixir Interfaces, this is how you get this “shared interface” in functional programming.
Difference with using
interface
?Ahh, I was confusing with the GraphQL interface through the Absinthe implementation. In GraphQL (which Absinthe implements in Elixir), interfaces define a shared set of fields that different types can implement.
https://elixirschool.com/en/lessons/advanced/behaviours
Sometimes you want modules to share a public API, the solution for this in Elixir is behaviours. Behaviours perform two primary roles:
- Defining a set of functions that must be implemented
- Checking whether that set was actually implemented
This is the behaviour we want to see:
defmodule Example.Worker do
@callback init(state :: term) :: {:ok, new_state :: term} | {:error, reason :: term}
@callback perform(args :: term, state :: term) ::
{:ok, result :: term, new_state :: term}
| {:error, reason :: term, new_state :: term}
end
Then, we can have the implementation
defmodule Example.Downloader do
@behaviour Example.Worker
def init(opts), do: {:ok, opts}
def perform(url, opts) do
url
|> HTTPoison.get!()
|> Map.fetch(:body)
|> write_file(opts[:path])
|> respond(opts)
end
defp write_file(:error, _), do: {:error, :missing_body}
defp write_file({:ok, contents}, path) do
path
|> Path.expand()
|> File.write(contents)
end
defp respond(:ok, opts), do: {:ok, opts[:path], opts}
defp respond({:error, reason}, opts), do: {:error, reason, opts}
end