Elixir basics · Lesson 22 (open contents)
01 · Let the code speak02 · Meet six kinds of values03 · Put values together04 · Build a new list05 · Make the shapes match06 · Choose with case07 · Parse a number safely08 · Unpack &1 and the pipe09 · Put code in a project10 · Ask precise true-or-false questions11 · Read and update nested data12 · Count visible text and bytes13 · Let function clauses choose14 · Solve a list twice15 · Choose the smallest clear control form16 · Give domain data a shape17 · Put one tested module in Mix18 · Make success and failure predictable19 · Read and write one real file20 · Take only the data you need21 · Give different data one shared action22 · Write a callback contract23 · Write down the data contract24 · Test behavior and boundaries25 · Turn a project into a command26 · Write the project promise27 · Parse one real line28 · Keep the public API small29 · Make the promise executable30 · Package the edge and leave clues31 · Prove the project is done
ELIXIR · INTERMEDIATE · LESSON 2225 minutes

Write a callback contract

Define a generic behaviour and make a module satisfy it.

01 · Start with the whole example

Run this first

Run it once, change one input, and compare the new result.

Elixir
# 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")
Check the result first
  • The call returns "Hello, Maya". No process or GenServer is involved.
02 · Take the code apart

Read from the first line down

  1. Read @callback: Declare one required function signature.
  2. Read @behaviour: State which contract a module implements.
  3. Read term(): Accept or return any Erlang term in a typespec.
03 · Meet the new symbols

Symbols are not secret signs

@callback

Declare one required function signature.

@behaviour

State which contract a module implements.

term()

Accept or return any Erlang term in a typespec.

04 · Ideas inside the code

Match each name to its meaning

01

behaviour

A behaviour declares callbacks that implementing modules promise to provide.

02

callback

A callback gives a function name, arguments, and return type expected by the contract.

03

implementation module

@behaviour asks the compiler to check that required callbacks exist.

05 · Make the idea clear

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.

DESIGN DECISION

Why this shape?

Why does a behaviour define callbacks without creating a process?

Choice

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.

Cost and boundary

It standardizes a boundary. It does not choose state ownership, supervision, capacity, or error policy for you.

A FAMILIAR POINT OF VIEW

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.
06 · Change it yourself

Close the answer and try

Add a Formal module that implements the same callback.

Practice starting point
defmodule Formal do
  @behaviour ____
  @impl true
  def greet(name), do: "Good day, #{____}"
end

Target result: Formal.greet("Maya") should return "Good day, Maya".

Stuck? Read one hint

Use Greeter and name.

After you run it, see one answer
One answer
defmodule Formal do
  @behaviour Greeter
  @impl true
  def greet(name), do: "Good day, #{name}"
end
Think about it: Does every behaviour start a process?

No. A behaviour is a callback contract; OTP behaviours add process machinery later.

Take with you

Remember these three lines

  1. 1

    Behaviours define module contracts.

  2. 2

    Callbacks are checked at compile time.

  3. 3

    Keep generic contracts separate from OTP process lessons.

Lesson completeRun the code once, then mark this lesson as done.