Control Flow

Elixir conditionals — if, unless, case, cond, with, and try/rescue

if / unless

# if returns a value
result = if score > 90, do: :a, else: :b

# Multi-line
if logged_in? do
  render_dashboard()
else
  render_login()
end

# unless is the opposite of if
unless is_nil(user) do
  greet(user)
end

case

Matches a value against multiple patterns. Use when you need to destructure data.

case response do
  {:ok, data} -> process(data)
  {:error, :not_found} -> {:error, "Not found"}
  {:error, reason} -> {:error, reason}
end

# With guards
case value do
  n when n > 0 -> :positive
  0 -> :zero
  n when n < 0 -> :negative
end

Tip: Always include a catch-all clause (_) in case to avoid CaseClauseError for unexpected values.

cond

Evaluates conditions in order, returns the first truthy result.

cond do
  temperature > 30 -> :hot
  temperature > 20 -> :warm
  temperature > 10 -> :cool
  true -> :cold          # catch-all (like else)
end

with

Chain pattern matches; if any match fails, the else clause handles it.

with {:ok, user} <- fetch_user(id),
     {:ok, token} <- create_token(user),
     {:ok, session} <- create_session(token) do
  {:ok, session}
else
  {:error, :not_found} -> {:error, "User not found"}
  {:error, reason} -> {:error, reason}
end

Tip: Without an else clause, with returns the first non-matching value, which is useful for bubbling up errors.

try / rescue / after

try do
  risky_operation()
rescue
  e in ArgumentError -> {:error, e.message}
  e in [File.Error, RuntimeError] -> {:error, "Something went wrong"}
after
  cleanup()  # always runs
end

Convention: In Elixir, prefer pattern matching and {:ok, result} / {:error, reason} tuples over try/rescue. Reserve rescue for truly exceptional cases or interfacing with Erlang code that raises.

throw / catch

Rarely used — only for interfacing with Erlang libraries:

try do
  throw(:break)
catch
  :throw, :break -> :stopped
end

raise

# Raise a runtime error
raise "Something went wrong"

# Raise a custom error type
raise ArgumentError, "Invalid input: #{input}"

# Custom exception
defmodule MyError do
  defexception [:message, :code]
end

raise MyError, message: "failed", code: 500