Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Erlang error handling philosophy - case vs throw

I'm writing a REST service in Erlang and need to verify the received data before passing it to other internal functions for further processing; in order to do that, I'm currently using nested case expressions like this:

case all_args_defined(Args) of
    true ->
        ActionSuccess = action(Args),

        case ActionSuccess of
            {ok, _} -> ...;
            {fail, reason} -> {fail, reason}
        end,
    _ ->
        {fail, "args not defined"}
end,
...

I realize this is kind of ugly, but this way I can provide detailed error messages. Additionally, I don't think the usual make it crash philosophy is applicable here - I don't want my REST service to crash and be restarted every time somebody throws invalid arguments at it.

However, I'm considering abandoning all those cases in favor of an umbrella try/catch block catching any badmatch errors - would this work?

fun() ->
    true = all_args_defined(Args),
    {ok, _} = action(Args).

%% somewhere else
catch fun().
like image 894
Philip Kamenarsky Avatar asked Jul 28 '11 13:07

Philip Kamenarsky


People also ask

What is the difference between error handling and exception handling in C#?

The main difference between Error and Exception in C# is that an Error occurs due to unavailability of a system resource while an Exception occurs due to an issue in the program.

What is centralized error handling?

Centralized way involves some sort of central database or storage of errors (usually a module or a header file) where are all the error messages stored. In code you pass an error code to some function and it does all the work for you. A big plus of this approach is that you have everything in one place.

What is the Let it crash philosophy?

The Let It Crash philosophy is an approach to error handling that seeks preserve the integrity and reliability of a system by intentionally allowing certain faults to go unhandled.


1 Answers

Since what you want to achieve is error reporting, you should structure the thing around the execution of actions and reporting of the result. Perhaps something like this:


  execute(Action, Args) ->
    try
      check_args(Args),
      Result = action(Action, Args),
      send_result(Result)
    catch
      throw:{fail, Reason} ->
        report_error(Reason);
      ExceptionClass:Term ->
        %% catch-all for all other unexpected exceptions
        Trace = erlang:get_stacktrace(),
        report_error({crash, ExceptionClass, Term, Trace})
    end.

  %% all of these throw {fail, Reason} if they detect something fishy
  %% and otherwise they return some value as result (or just crash)
  action(foo, [X1, X2]) -> ...;
  action(foo, Args) -> throw({fail, {bad_arity, foo, 2, Args}});
  action(...) -> ...

  %% this handles the formatting of all possible errors 
  report_error({bad_arity, Action, Arity, Args}) ->
    send_error(io_lib:format("wrong number of arguments for ~w: "
                             "expected ~w, but got ~w",
                             [Action, Arity, length(Args)]));
  report_error(...) -> ...;
  report_error({crash, Class, Term, Trace}) ->
    send_error(io_lib:format("internal error: "
                             "~w:~w~nstacktrace:~n~p~n",
                             [Class, Term, Trace])).
like image 79
RichardC Avatar answered Nov 14 '22 21:11

RichardC