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().
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.
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.
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.
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])).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With