Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I elegantly check many conditions in Erlang?

Tags:

erlang

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:

  • verify email not in use
  • verify username not in use
  • verify username is alphanumeric
  • verify all fields are above X characters long
  • verify all fields are less than Y characters long

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?

like image 452
ryeguy Avatar asked Mar 20 '09 13:03

ryeguy


2 Answers

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.

like image 83
Hynek -Pichi- Vychodil Avatar answered Oct 15 '22 17:10

Hynek -Pichi- Vychodil


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.

like image 4
JLarky Avatar answered Oct 15 '22 16:10

JLarky