Functions and Modules
How Elixir organizes code into modules and named functions, including multiple clauses and default arguments.
2 min read
Every named function in Elixir lives inside a module — there's no such thing as a free-floating top-level function outside of scripts. Modules are how Elixir namespaces and organizes code.
Defining a module and functions
defmodule Calculator do
def add(a, b) do
a + b
end
def subtract(a, b) do
a - b
end
end
Calculator.add(2, 3)
# => 5def defines a public function, callable from outside the module. defp defines a private one, only callable from within the same module:
defmodule Calculator do
def double_sum(a, b) do
sum(a, b) * 2
end
defp sum(a, b) do
a + b
end
end
Calculator.double_sum(2, 3)
# => 10
Calculator.sum(2, 3)
# ** (UndefinedFunctionError)Multiple clauses via pattern matching
A function can have several clauses with the same name and arity, each matching a different pattern of arguments — Elixir tries them top to bottom and runs the first one that matches:
defmodule Greeter do
def greet(:morning), do: "Good morning!"
def greet(:evening), do: "Good evening!"
def greet(_other), do: "Hello!"
end
Greeter.greet(:morning)
# => "Good morning!"
Greeter.greet(:afternoon)
# => "Hello!"This is a core Elixir idiom: instead of one function body full of if/case branches, you often express branching as separate clauses, each handling one shape of input. It reads declaratively — each clause says exactly which input it's for.
defmodule ListInfo do
def describe([]), do: "empty list"
def describe([_single]), do: "one item"
def describe(list), do: "#{length(list)} items"
endArity: part of a function's identity
A function's arity (its number of arguments) is part of its identity in Elixir — greet/1 and greet/2 are considered entirely different functions, even with the same name. You'll see this name/arity notation throughout Elixir documentation and error messages.
Default arguments
The \\ operator supplies a default value for a parameter:
defmodule Greeter do
def greet(name, greeting \\ "Hello") do
"#{greeting}, #{name}!"
end
end
Greeter.greet("Ada")
# => "Hello, Ada!"
Greeter.greet("Ada", "Hey")
# => "Hey, Ada!"When a function with defaults also has multiple clauses, Elixir requires the default to be declared once in a header-only clause, to avoid ambiguity about which clause "owns" it.
Module attributes and documentation
defmodule Circle do
@moduledoc "Functions for working with circles."
@pi 3.14159
@doc "Calculates the area of a circle given its radius."
def area(radius) do
@pi * radius * radius
end
end@moduledoc and @doc aren't just comments — they're compiled into the module and picked up by tools like mix docs and h Circle.area inside iex, which is why well-documented Elixir libraries feel so consistent to explore. @pi here is a module attribute, a compile-time constant inlined wherever it's referenced.
Modules and functions are the basic unit of organization in every Elixir codebase, from a five-line script up to a full Phoenix application — everything else in this course builds on grouping related behavior into modules exactly like this.
Test what you just learned
4 quick questions. Get all of them right to unlock the next lesson.
You can take the quiz without an account — logging in just lets your result count toward your progress.