Errors in Erlang

In Erlang, it’s idiomatic to communicate errors via pattern matching and the use of tagged tuples. This approach makes it easy to see which functions return errors and to handle them using the same language constructs employed for other, non-error tasks.

-module(errors).
-export([main/0]).

% By convention, errors are often returned as {error, Reason} tuples.
f(Arg) ->
    case Arg of
        42 ->
            % We return a tuple with 'error' atom and a reason string.
            {error, "can't work with 42"};
        _ ->
            % For successful cases, we return an 'ok' tuple with the result.
            {ok, Arg + 3}
    end.

% Predefined errors can be atoms or tuples.
-define(ERR_OUT_OF_TEA, out_of_tea).
-define(ERR_POWER, {error, cant_boil_water}).

make_tea(Arg) ->
    case Arg of
        2 ->
            ?ERR_OUT_OF_TEA;
        4 ->
            % In Erlang, we don't wrap errors, but we can create nested tuples
            % to provide more context.
            {error, {making_tea, ?ERR_POWER}};
        _ ->
            ok
    end.

main() ->
    % Using lists:foreach for iteration
    lists:foreach(fun(I) ->
        case f(I) of
            {error, E} ->
                io:format("f failed: ~p~n", [E]);
            {ok, R} ->
                io:format("f worked: ~p~n", [R])
        end
    end, [7, 42]),

    lists:foreach(fun(I) ->
        case make_tea(I) of
            ?ERR_OUT_OF_TEA ->
                io:format("We should buy new tea!~n");
            {error, {making_tea, ?ERR_POWER}} ->
                io:format("Now it is dark.~n");
            {error, E} ->
                io:format("unknown error: ~p~n", [E]);
            ok ->
                io:format("Tea is ready!~n")
        end
    end, lists:seq(0, 4)).

In Erlang, we use pattern matching extensively for error handling. The case expression is used to match on the return value of functions and handle different cases accordingly.

To run this program, save it as errors.erl and use the Erlang shell:

$ erl
1> c(errors).
{ok,errors}
2> errors:main().
f worked: 10
f failed: "can't work with 42"
Tea is ready!
Tea is ready!
We should buy new tea!
Tea is ready!
Now it is dark.
ok

Erlang’s approach to error handling is quite different from languages that use exceptions. Instead of try-catch blocks, Erlang encourages the use of pattern matching and the “let it crash” philosophy, where processes are allowed to fail and are restarted by supervisors.