[erlang-questions] Idiomatically handling multiple validation checks

Dmytro Lytovchenko dmytro.lytovchenko@REDACTED
Tue Dec 6 10:34:21 CET 2016


2016-12-06 10:25 GMT+01:00 zxq9 <zxq9@REDACTED>:

> On 2016年12月6日 火曜日 10:13:34 Dmytro Lytovchenko wrote:
> > For things like web form or input validation and transformation, it is
> > often a good idea to make a chain of nested calls.
> >
> > Imagine your user submitted a form which you store in Data = #{login="",
> > password=""}, you can do something like this:
> >
> > try
> >     create_session(check_password(check_login(Data)))
> > catch throw:{validation_error, E} -> my_report_error(E)
> > end,...
> >
> >
> > Here each function (check_login, check_password, create_session) takes
> > result of the previous function, it can be that same Data, or Data paired
> > with some intermediate results if you wish. To signal an error you use
> > erlang:throw({validation_error, "my error"}) and it will be caught in
> the
> > catch clause.
>
> That's one way to do it... but most code I've seen that makes heavy use
> of `throw` has insane structural issues (either actual bugs, or mental
> grenades with C++ style confusing execution).
>
> Basically, what I'm getting at is that `throw` is a keyword oft used in
> prayer and curse in Mordor.
>

Any best practice can be corrupted and written in a horrible way, it's a
matter of self discipline and good reviewing in the team. Best idea is to
not allow throw propagate more than 1 logical level up. We chain-call the
handlers, and the handlers are allowed to throw, we catch it. Anything that
handlers can call and that can throw, should be caught by them and not
propagate wildly onto higher levels.


>
> Why not return `{error, Reason}` back up the chain of calls? Then the
> top-level caller can choose whether to crash on an `{ok, Value}` or
> `{error, Reason}` assertion match, or engage in their own insanity with
> `throw` .. `catch` and other nonsense


Absolutely it can be done Haskell/ML way with values wrapped as {ok,_} or
{error, _} and returned. This will make beautiful flow and will triple the
chain runner function size, or you will have to add an {error, _} clause to
each of your handlers. And exceptions still are going to happen, so those
have to be caught and wrapped with {error, _} too.

On the other hand throw was designed as a way for user to safely interrupt
whatever was running and hop out of deep nested calls. Using it wisely and
keeping it simple may produce clean short code.

For simplicity i kept my example short.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20161206/9f01969f/attachment.htm>


More information about the erlang-questions mailing list