Control Flow

if is a macro; it returns nil when the condition is falsy and there is no else.

9.1 case

case File.read("config.txt") do
  {:ok, contents} -> String.length(contents)
  {:error, :enoent} -> IO.puts("not found"); 0
  {:error, reason} -> raise "failed: #{inspect(reason)}"
end

case x do
  n when n > 10 -> :big
  _ -> :small
end

9.2 cond — first truthy condition

cond do
  temp < 0 -> "freezing"
  temp < 20 -> "cold"
  temp < 30 -> "warm"
  true -> "hot"           # default branch
end

9.3 if / unless

if is a macro; it returns nil when the condition is falsy and there is no else.

if age >= 18, do: "adult", else: "minor"

if logged_in? do
  show_dashboard()
else
  redirect()
end

unless valid?, do: raise("invalid")

9.4 with — chaining matches

If any clause fails to match, with returns that value (or goes to else). Guards allowed: {:ok, n} when n > 0 <- get(). Bare expressions: x = compute().

with {:ok, user} <- fetch_user(id),
     {:ok, account} <- fetch_account(user),
     :ok <- check_balance(account, amount) do
  transfer(account, amount)
else
  {:error, :not_found} -> {:error, "user not found"}
  {:error, reason} -> {:error, reason}
end

9.5 Everything is an expression

Variables bound inside if/case don't leak out — return values instead:

result = if x > 0, do: :pos, else: :neg
# Wrong (doesn't work):
if cond, do: x = 1
# Right:
x = if cond, do: 1, else: 0