In simple terms the “behaviour” already exists as the behaviour module capturing the generic parts. You are implementing the callback module which contains the specific parts. To execute those specific parts the behaviour module calls the “callback” functions on the callback module.
The behaviour module is passed a callback module much in the same way a function is passed a callback function - the difference is that the behaviour module expects certain functions to be implemented by the callback module rather than there just being that one function.
defmodule Parser do
@callback parse(String.t) :: {:ok, term} | {:error, String.t}
@callback extensions() :: [String.t]
def parse!(callback_module, contents) do
case callback_module.parse(contents) do
{:ok, data} -> data
{:error, error} -> raise ArgumentError, "parsing error: #{error}"
end
end
end
I swapped implementation
with callback_module
- so it makes sense that callback_module.parse/1
is a callback function.
Another example from the Access behaviour
def fetch(%module{} = container, key) do
module.fetch(container, key)
rescue
exception in UndefinedFunctionError ->
raise_undefined_behaviour(exception, module, {^module, :fetch, [^container, ^key], _})
end
The fetch/2
function implemented by Access
grabs the callback module from the struct
so it can turn around and call the module.fetch/2
callback function.
With behaviours you are composing the callback module with the behaviour module - there is no inheritance.