[erlang-questions] How do I elegantly check many conditions?

Richard O'Keefe ok@REDACTED
Mon Mar 23 04:13:35 CET 2009


On 21 Mar 2009, at 2:20 am, ryeguy wrote:

> 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?

For concreteness, let's suppose we have
	email_is_available(Info)      -> ok | {no,Reason}
	user_syntax_ok(Info)          -> ok | {no,Reason}
	user_is_available(Info)       -> ok | {no,Reason}
	all_fields_long_enough(Info)  -> ok | {no,Reason}
	all_fields_short_enough(Info) -> ok | {no,Reason}
I'm assuming here that
(a) the field size and syntax checks should be done first,
     as they just involve the data to hand
(b) the availability checks should only be done with verified data
(c) when a check fails you want the reason.

In short, we want to compose checkers.  So let's try that.

compose_check(F1, F2) ->
     fun (Info) ->
         case F1(Info)
           of ok -> F2(Info)
            ; Failure -> Failure
	end
     end.

Now we can do

compose_check_list(Check_Funs) ->
     lists:foldl(fun compose_check/2, fun (_) -> ok end, Check_Funs).

and
     compose_check_list([
	fun all_fields_long_enough/1,
	fun all_fields_short_enough/1,
	fun user_syntax_ok/1,
	fun email_is_ available/1,
	fun user_is_available/1
     ])(Info)

Or we can avoid creating the intermediate functions and write

check_all(_, []) -> ok;
check_all(Info, [F|Fs]) ->
     case F(Info)
       of ok -> check_all(Info, Fs)
        ; Failure -> Failure
     end.
...
     check_all(Info, [
	fun all_fields_long_enough/1,
	fun all_fields_short_enough/1,
	fun user_syntax_ok/1,
	fun email_is_available/1,
	fun user_is_available/1])


Or you could arrange your checkers to either return ok or
to throw {check_failed,Reason}, and do

	try
	    all_fields_long_enough(Info),
	    all_fields_short_enough(Info),
	    user_syntax_ok(Info),
	    email_is_available(Info),
	    user_is_available(Info)
	catch
	    throw:{check_failed,Reason} ->
		{no,Reason}
	end





More information about the erlang-questions mailing list