Validating the arguments of an Erlang function can lead to severely nested code. Let’s say we have two different functions that must be called to validate our data, and we want to throw a different exception depending on which one fails. One possible way to code this is:
my_funtion(Data) ->
case is_valid_A(Data) of
true ->
case is_valid_B(Data) of
true ->
...function body...
false ->
throw({error, validator_B_failed})
end;
false ->
throw({error, validator_A_failed})
end.
This is ugly for a few reasons:
is_valid_C/1
, is_valid_D/1
) will require even
more indention and a bit of thought. While there is a pattern to follow,
it’s not a very clear pattern, and it requires us to touch many lines of code.A better option is to use the ‘orelse’ operator to throw the exception if something fails. We can rewrite the above code as follows:
my_funtion(Data) ->
is_valid_A(Data) orelse throw({error, validator_A_failed}),
is_valid_B(Data) orelse throw({error, validator_B_failed}),
... function body ...
This works because the orelse
operator is short-circuiting. The
system evaluates the first clause. If the result is true
, then no
other statements are evaluated. If the result is false
, then the
system evaluates the next clause, which throw
s our exception.
By structuring the code like this we save a lot of typing, make the intentions of the code clearer, provide an easy pattern for adding future validations if necessary, and make it easy to comment out validators quickly.
Content © 2006-2021 Rusty Klophaus