How do I elegantly check many conditions in Erlang?

前端 未结 4 1803
清歌不尽
清歌不尽 2020-12-23 15:28

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

相关标签:
4条回答
  • 2020-12-23 15:48

    Maybe you will need using of

    receive
        message1 -> code1;
        message2 -> code2;
        ...
    end.
    

    But, of course, there will be spawn() methods.

    0 讨论(0)
  • 2020-12-23 15:55
    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.

    0 讨论(0)
  • 2020-12-23 16:01

    Building up on @JLarky's answer, here's something that i came up with. It also draws some inspiration from Haskell's monads.

    -record(user,
        {name :: binary(), 
         email :: binary(), 
         password :: binary()}
    ).
    -type user() :: #user{}.
    -type bind_res() :: {ok, term()} | {error, term()} | term().
    -type bind_fun() :: fun((term()) -> bind_res()).
    
    
    -spec validate(term(), [bind_fun()]) -> bind_res().
    validate(Init, Functions) ->
        lists:foldl(fun '|>'/2, Init, Functions).
    
    -spec '|>'(bind_fun(), bind_res())-> bind_res().
    '|>'(F, {ok, Result}) -> F(Result);
    '|>'(F, {error, What} = Error) -> Error;
    '|>'(F, Result) -> F(Result).
    
    -spec validate_email(user()) -> {ok, user()} | {error, term()}. 
    validate_email(#user{email = Email}) ->
    ...
    -spec validate_username(user()) -> {ok, user()} | {error, term()}.
    validate_username(#user{name = Name}) ->
    ...
    -spec validate_password(user()) -> {ok, user()} | {error, term()}.    
    validate_password(#user{password = Password}) ->
    ...
    
    validate(#user{...}, [
        fun validate_email/1,
        fun validate_username/1,
        fun validate_password/1
    ]).
    
    0 讨论(0)
  • 2020-12-23 16:07

    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.

    0 讨论(0)
提交回复
热议问题