F3.06 · part 3 of 3
Behaviours & callbacks
A behaviour is the other half of polymorphism. Where a protocol dispatches on a value's type at
runtime, a behaviour is a contract on a module: @callback declares the
functions a module must provide, @behaviour opts a module in, and the compiler checks
that every callback is implemented.
The portal sends notifications through interchangeable modules — email, SMS, push. A
Portal.Notifier behaviour names what each must implement. Choose a module and see
whether it satisfies the contract.
A contract the compiler checks
The behaviour declares two callbacks: deliver/2 and
valid_target?/1. A module that adopts it must define both. Choose a notifier and see
which callbacks it provides — and what the compiler says when one is absent.
The implementing module · select one
The contract is enforced at compile time, on the module — a missing callback is a warning before the code ever runs. That is the mirror image of a protocol, whose dispatch is decided at the call.
Declaring callbacks
@callback gives each required function a typed signature. An implementing module adds
@behaviour and marks each definition @impl true, which both
documents intent and lets the compiler catch a name or arity that drifts from the contract.
defmodule Portal.Notifier do @callback deliver(target :: String.t(), message :: String.t()) :: {:ok, term} | {:error, term} @callback valid_target?(target :: String.t()) :: boolean end defmodule EmailNotifier do @behaviour Portal.Notifier @impl true def deliver(to, msg), do: {:ok, "email to #{to}"} @impl true def valid_target?(to), do: String.contains?(to, "@") end
Two kinds of polymorphism
Choose behaviour by the value passed in, or fix a contract that many modules each fulfil.
In Elixir
Protocols dispatch on a value at runtime; behaviours are a compile-time module contract — and OTP's GenServer and Supervisor are behaviours.
That completes protocols and behaviours: one dispatches on data, the other constrains modules. Next module: F3.07 — Processes & the actor model, where a module adopting a behaviour becomes a running, message-passing process.
References
Primary sources for this lesson, and where it connects in the course.
Sources
- Typespecs and behaviours — Elixir documentation — contracts via behaviours.
- Protocols — Elixir documentation — polymorphism by data type.
Protocol— Elixir documentation — defining and consolidating protocols.