So when a user sends a request to register an account, they send their username, password, email, and other info. The registration function must verify all of their data. An example would be:
Now I don't want to have a 5 level deep if or case statement, but what other options do I have? Splitting it into separate functions sounds like a good idea, but then I just have to check the return value of the functions in some sort of conditional and it's back to the original problem.
I could separate them into functions and then call an if statement with all of the conditionals OR'd together, but that wouldn't give me what I want because I need to be able to tell the user the specific error if there was one.
How does one handle this kind of situation in erlang? Is there an equivalent of a return statement, or does it have to be the last executable line in a function to be a return value?
One of Joe Armstrong's suggestion: program success case code separated from error handling. You can make it in this way
create_user(Email, UserName, Password) ->
try
ok = new_email(Email),
ok = valid_user_name(UserName),
ok = new_user(UserName),
ok = strong_password(Password),
...
_create_user(Email, UserName, Password)
catch
error:{badmatch, email_in_use} -> do_something();
error:{badmatch, invalid_user_name} -> do_something();
error:{badmatch, user_exists} -> do_something();
error:{badmatch, weak_password} -> do_something();
...
end.
note that you can do all errors catches out of create_user function which is better.
create_user(Email, UserName, Password) ->
ok = new_email(Email),
ok = valid_user_name(UserName),
ok = new_user(UserName),
ok = strong_password(Password),
...
_create_user(Email, UserName, Password).
main() ->
try
...
some_function_where_create_user_is_called(),
...
catch
...
error:{badmatch, email_in_use} -> do_something();
error:{badmatch, invalid_user_name} -> do_something();
error:{badmatch, user_exists} -> do_something();
error:{badmatch, weak_password} -> do_something();
...
end.
Pattern match is one of coolest things in Erlang. Note that you can involve your tag to badmatch error
{my_tag, ok} = {my_tag, my_call(X)}
and custom data too
{my_tag, ok, X} = {my_tag, my_call(X), X}
If exception is fast enough for you depends of your expectations. Speed on my 2.2GHz Core2 Duo Intel: about 2 millions exceptions in one second (0.47us) compared to 6 millions success (external) function calls (0.146us) - one can guess that exception handling takes about 0.32us. In native code it is 6.8 vs 47 millions per second and handling can take about 0.125us. There can be some additional cost for try-catch construct which is about 5-10% to success function call in both native and byte-code.
User = get_user(),
Check_email=fun(User) -> not is_valid_email(User#user.email) end,
Check_username=fun(User) -> is_invalid_username(User#user.name) end,
case lists:any(fun(Checking_function) -> Checking_function(User) end,
[Check_email, Check_username, ... ]) of
true -> % we have problem in some field
do_panic();
false -> % every check was fine
do_action()
end
So it isn't 5 level deep any more. For real program i guess you should use lists:foldl for accumulate error message from every checking function. Because for now it simple says 'all fine' or 'some problem'.
Note that in this way add or remove checking condition isn't a big deal
And for "Is there an equivalent of a return statement..." - look at try catch throw statement, throw acts like return in this case.
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