Elixir basics · Lesson 22 (open contents)
Write a callback contract
Define a generic behaviour and make a module satisfy it.
Run this first
Run it once, change one input, and compare the new result.
# Define a contract without creating a process
defmodule Greeter do
@callback greet(String.t()) :: String.t()
end
defmodule Friendly do
@behaviour Greeter
@impl true
def greet(name), do: "Hello, #{name}"
end
Friendly.greet("Maya")- The call returns
"Hello, Maya". No process or GenServer is involved.
Read from the first line down
- Read
@callback: Declare one required function signature. - Read
@behaviour: State which contract a module implements. - Read
term(): Accept or return any Erlang term in a typespec.
Symbols are not secret signs
@callbackDeclare one required function signature.
@behaviourState which contract a module implements.
term()Accept or return any Erlang term in a typespec.
Match each name to its meaning
behaviour
A behaviour declares callbacks that implementing modules promise to provide.
callback
A callback gives a function name, arguments, and return type expected by the contract.
implementation module
@behaviour asks the compiler to check that required callbacks exist.
Why these forms are useful
Run the example first. Predict one result, then change one input and run it again.
A behaviour declares callbacks that implementing modules promise to provide.
A callback gives a function name, arguments, and return type expected by the contract.
Why this shape?
Why does a behaviour define callbacks without creating a process?
A behaviour first states which functions a module must implement. GenServer/gen_server is a behaviour with a runtime protocol, but a general behaviour starts nothing by itself.
It standardizes a boundary. It does not choose state ownership, supervision, capacity, or error policy for you.
Coming from Java, Python, or JavaScript
Java
- Familiar starting point
- An interface defines a method contract.
- What BEAM changes
- A behaviour constrains module/function/arity entries, which callers still invoke as module functions.
- False friend
- Implementing one creates neither an object nor a thread.
Python
- Familiar starting point
- An ABC or Protocol.
- What BEAM changes
- Callback metadata helps tools check an implementation and lets OTP invoke it.
- False friend
- It is not a decorator that wraps runtime calls.
JavaScript
- Familiar starting point
- A conventional object shape or TypeScript interface.
- What BEAM changes
- A behaviour lives at the BEAM module and callback-entry level.
- False friend
- A type contract and a process lifecycle remain separate decisions.
Close the answer and try
Add a Formal module that implements the same callback.
defmodule Formal do
@behaviour ____
@impl true
def greet(name), do: "Good day, #{____}"
endTarget result: Formal.greet("Maya") should return "Good day, Maya".
Stuck? Read one hint
Use Greeter and name.
After you run it, see one answer
defmodule Formal do
@behaviour Greeter
@impl true
def greet(name), do: "Good day, #{name}"
endThink about it: Does every behaviour start a process?
No. A behaviour is a callback contract; OTP behaviours add process machinery later.
Remember these three lines
- 1
Behaviours define module contracts.
- 2
Callbacks are checked at compile time.
- 3
Keep generic contracts separate from OTP process lessons.