पाठ 7 / 25
case, cond, if and with
Choose the right control-flow construct and chain fallible steps with with.
Branching in a functional language
case matches a value against patterns (with optional guards) and is the workhorse for handling results: case fetch(id) do {:ok, user} -> ...; {:error, :not_found} -> ... end. cond checks a series of boolean conditions and runs the first true one, useful when you have ranges or unrelated tests; end with true -> as a catch-all. if and unless handle a single condition, and only false and nil are falsy. All of these are expressions that return values. with chains several steps that each must match a pattern: if every clause matches, the do block runs; if one fails, the non-matching value is returned (or handled in an else block). It is the idiomatic way to write a happy path through several {:ok, value} steps without nested case statements. Prefer multi-clause functions when branching on the shape of an argument, case for results of a call, cond for multiple conditions and with for sequences of fallible steps.
The with happy path
Each step must match {:ok, value}; the first failure exits to else.
Placing an order with with
Each step returns {:ok, value} or {:error, reason}.
defmodule Shop.Checkout do
def place_order(params) do
with {:ok, customer} <- find_customer(params["customer_id"]),
{:ok, items} <- validate_items(params["items"]),
total = Enum.sum(Enum.map(items, & &1.price * &1.qty)),
:ok <- check_credit(customer, total) do
{:ok, %{customer: customer.name, total: total}}
else
{:error, :not_found} -> {:error, "customer not found"}
{:error, :empty} -> {:error, "cart is empty"}
{:error, :credit_limit} -> {:error, "credit limit exceeded"}
end
end
defp find_customer("c-1"), do: {:ok, %{id: "c-1", name: "Asha", limit: 500_000}}
defp find_customer(_), do: {:error, :not_found}
defp validate_items([]), do: {:error, :empty}
defp validate_items(nil), do: {:error, :empty}
defp validate_items(items), do: {:ok, items}
defp check_credit(%{limit: limit}, total) when total <= limit, do: :ok
defp check_credit(_customer, _total), do: {:error, :credit_limit}
def shipping_label(total) do
cond do
total >= 100_000 -> "free"
total >= 50_000 -> "discounted"
true -> "standard"
end
end
end
Shop.Checkout.place_order(%{"customer_id" => "c-1", "items" => [%{price: 4_950, qty: 2}]})
|> IO.inspect() # {:ok, %{customer: "Asha", total: 9900}}Keep with else clauses exhaustive
If a with step returns something that no else clause matches, Elixir raises WithClauseError. Make step functions return a small, known set of error tuples, or add a final catch-all clause.
त्वरित जाँच: What happens when a clause inside with does not match?
- with stops and returns the non-matching value, or passes it to the else block
- The program crashes always
- The clause is skipped and the next one runs
- It retries the clause
Answer
with stops and returns the non-matching value, or passes it to the else block — with short-circuits on the first non-matching value.