Elixir is a dynamic, functional language designed for building scalable, maintainable, and fault-tolerant applications on the Erlang VM (BEAM).
🔗 Related: Elixir Metaprogramming Cheat Sheet · Absinthe GraphQL Cheat Sheet
Getting Started
Hello World
# hello.exs
defmodule Greeter do
def greet(name) do
message = "Hello, " <> name <> "!"
IO.puts(message)
end
end
Greeter.greet("world")
Run via the Elixir CLI:
elixir hello.exs
# => Hello, world!
Variables & Immutability
age = 23
age = 24 # Rebinding (does not mutate original value)
Maps
user = %{
name: "John",
city: "Melbourne"
}
IO.puts("Hello, " <> user.name)
Lists
users = ["Tom", "Dick", "Harry"]
Enum.each(users, fn user ->
IO.puts("Hello " <> user)
end)
Pipe Operator (|>)
Takes the result of the left expression and passes it as the first argument to the right function call:
source
|> transform(:hello)
|> print()
# Equivalent to:
print(transform(source, :hello))
Pattern Matching
The = operator is the match operator:
user = %{name: "Tom", age: 23}
%{name: username} = user
# username is bound to "Tom"
# Pin operator (^) matches against existing value without rebinding
x = 1
^x = 1 # Success
# ^x = 2 # MatchError
Pattern Matching in Function Heads
defmodule UserGreeter do
def greet(%{name: username}) do
IO.puts("Hello, " <> username)
end
end
Control Flow
if & unless
if status == :ok do
"Ready"
else
"Pending"
end
case
case {1, 2, 3} do
{4, 5, 6} -> "No match"
{1, x, 3} -> "Matches and binds x to 2"
_ -> "Wildcard matches any value"
end
cond
Evaluates conditions sequentially until one returns a truthy value:
cond do
1 + 1 == 3 -> "Never matches"
2 * 5 == 12 -> "Neither"
true -> "Matches (acts as else)"
end
with (Chained Happy Paths)
Safely pipelines multiple pattern matches and halts on first error:
with {:ok, {int, _}} <- Integer.parse("123abc"),
{:ok, datetime, _offset} <- DateTime.from_iso8601("2026-10-25T12:00:00Z") do
DateTime.add(datetime, int, :second)
else
:error -> "Invalid integer format"
{:error, :invalid_format} -> "Invalid ISO datetime"
_ -> "Unhandled error"
end
Types & Operators
Core Data Types
| Value Example | Data Type | Notes |
|---|---|---|
nil |
Nil / null | Falsy value |
true / false |
Boolean | Atom aliases (:true, :false) |
:hello |
Atom | Constant whose name is its value |
23 |
Integer | Arbitrary precision |
3.14 |
Float | 64-bit IEEE 754 |
"hello" |
Binary UTF-8 String | Double quotes |
'hello' |
Charlist | Single quotes (list of codepoints) |
[1, 2, 3] |
List | Linked list |
{1, 2} |
Tuple | Contiguous memory array |
%{a: 1} |
Map | Key-value dictionary |
Type Guards
Available inside when clauses:
is_atom/1 is_boolean/1 is_binary/1
is_integer/1 is_float/1 is_number/1
is_list/1 is_map/1 is_tuple/1
is_nil/1 is_function/1 is_pid/1
Collections & Data Structures
Maps
m = %{name: "Alice", age: 30}
# Update existing key syntax
m = %{m | age: 31}
# Map module functions
Map.get(m, :name) # "Alice"
Map.put(m, :city, "Tokyo") # Adds :city
Map.delete(m, :age) # Removes :age
Map.merge(m, %{active: true}) # Merges maps
Lists & Enums
list = [1, 2, 3, 4]
# Prepend (O(1)) and append (O(N))
[0 | list] # [0, 1, 2, 3, 4]
list ++ [5] # [1, 2, 3, 4, 5]
# Enum operations
Enum.map(list, &(&1 * 2)) # [2, 4, 6, 8]
Enum.filter(list, fn x -> rem(x, 2) == 0 end) # [2, 4]
Enum.reduce(list, 0, &+/2) # 10
Tuples & Keyword Lists
# Tuples
tuple = {:ok, "success"}
elem(tuple, 1) # "success"
# Keyword lists (list of two-element atom tuples)
opts = [color: "red", size: "large"]
opts[:color] # "red"
Functions
Anonymous Functions & Capture Syntax
# Anonymous function
add = fn a, b -> a + b end
add.(2, 3) # 5
# Capture syntax
square = &(&1 * &1)
square.(5) # 25
# Function reference capture
fun = &String.upcase/1
fun.("hello") # "HELLO"
Structs & Protocols
Defining Structs
defmodule User do
defstruct name: "", age: nil
end
user = %User{name: "Alice", age: 30}
user.name # "Alice"
Protocols
defprotocol Blank do
@doc "Returns true if data is empty"
def blank?(data)
end
defimpl Blank, for: List do
def blank?([]), do: true
def blank?(_), do: false
end
Blank.blank?([]) # true
Comprehensions (for)
# Basic list generator
for n <- 1..5, do: n * 2
# => [2, 4, 6, 8, 10]
# With filtering guard
for n <- 1..10, rem(n, 2) == 0, do: n
# => [2, 4, 6, 8, 10]
# Transform into Map
for {k, v} <- %{a: 1, b: 2}, into: %{}, do: {k, v * 10}
# => %{a: 10, b: 20}
Behaviours & Type Specs
defmodule Parser do
@callback parse(String.t()) :: {:ok, any()} | {:error, String.t()}
@callback extensions() :: [String.t()]
end
defmodule JSONParser do
@behaviour Parser
@impl Parser
def parse(str), do: {:ok, str}
@impl Parser
def extensions, do: [".json"]
end