Guards & Conditional Expressions

Guard expressions are restricted to side-effect-free operations: type tests, comparisons, arithmetic, boolean operators, and BIFs like abs, element, hd, tl, length, map_size, tuple_size, byte_size, bit_size, is_map_key, map_get, node, self, round, trunc, fl…

8.1 Guards

Guard expressions are restricted to side-effect-free operations: type tests, comparisons, arithmetic, boolean operators, and BIFs like abs, element, hd, tl, length, map_size, tuple_size, byte_size, bit_size, is_map_key, map_get, node, self, round, trunc, float, min, max (OTP 26+), binary_part. You cannot call your own functions in guards.

Errors in guards make the guard fail (no exception).

classify(N) when is_integer(N), N < 0 -> negative;     % , means AND
classify(0) -> zero;
classify(N) when is_integer(N) -> positive;
classify(N) when is_float(N); is_atom(N) -> other.     % ; means OR

8.2 case

If no clause matches → case_clause error.

describe(Value) ->
    case Value of
        {ok, Result} -> io:format("Success: ~p~n", [Result]);
        {error, Reason} when is_atom(Reason) -> io:format("Error: ~p~n", [Reason]);
        _ -> io:format("Unknown~n")
    end.

8.3 if (guard-based)

if branches use guards only (no function calls). If no branch matches → if_clause error.

temperature(T) ->
    if
        T < 0 -> freezing;
        T < 20 -> cold;
        T < 30 -> warm;
        true -> hot                % "else" branch
    end.

8.4 Everything is an expression

Variables bound in some branches of a case but not others are "unsafe" — the compiler warns. Bind in all branches or return values.

Status = case X > 0 of
             true -> positive;
             false -> non_positive
         end.

8.5 maybe expression (OTP 25+, default in 27)

?= short-circuits: if the match fails, the non-matching value is returned (or handled by else).

-feature(maybe_expr, enable).   % needed in OTP 25/26 only

process(Input) ->
    maybe
        {ok, Parsed} ?= parse(Input),
        {ok, Valid} ?= validate(Parsed),
        {ok, save(Valid)}
    else
        {error, Reason} -> {error, Reason}
    end.