Behaviours in Elixir (and Erlang) are a way to separate and abstract the generic part of a component (which becomes the behaviour module) from the specific part (which becomes the callback module).
A behaviour module defines a set of functions and macros (referred to as callbacks) that callback modules implementing that behaviour must export. This “interface” identifies the specific part of the component. For example, the
GenServer behaviour and functions abstract away all the message-passing (sending and receiving) and error reporting that a “server” process will likely want to implement from the specific parts such as the actions that this server process has to perform.
If a callback module that implements a given behaviour doesn’t export all the functions and macros defined by that behaviour, the user will be notified through warnings during the compilation process (no errors will happen).
A behaviour is always backed by a module (which is how the behaviour will be identified): the module where callbacks are defined. To define a behaviour module, it’s enough to define one or more callbacks in that module. To define callbacks, the
@macrocallback module attributes can be used (for function callbacks and macro callbacks respectively).
defmodule MyBehaviour do @callback my_fun(arg :: any) :: any @macrocallback my_macro(arg :: any) :: Macro.t end
As seen in the example above, defining a callback is a matter of defining a specification for that callback, made of:
- the callback name (
my_macroin the example)
- the arguments that the callback must accept (
arg :: anyin the example)
- the expected type of the callback return value
For more information on typespecs, consult the “Typespecs” page in the Elixir documentation. As mentioned in this page, type specification are only annotations used by documentation and tools, so defining such specifications for behaviours serves mostly for such purposes.
Optional callbacks are callbacks that callback modules may implement if they want to, but are not required to.
Usually, behaviour modules know if they should call those callbacks based on configuration, or they check if the callbacks are defined with
Optional callbacks can be defined through the
@optional_callbacks module attribute, which has to be a keyword list with function or macro name as key and arity as value. For example:
defmodule MyBehaviour do @callback vital_fun() :: any @callback non_vital_fun() :: any @macrocallback non_vital_macro(arg :: any) :: Macro.t @optional_callbacks non_vital_fun: 0, non_vital_macro: 1 end
One example of optional callback in Elixir’s standard library is
To specify that a module implements a given behaviour, the
@behaviour attribute must be used:
defmodule MyBehaviour do @callback my_fun(arg :: any) :: any end defmodule MyCallbackModule do @behaviour MyBehaviour def my_fun(arg), do: arg end