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

Gaspar Chilingarov nm@REDACTED
Fri Mar 20 18:29:25 CET 2009


Practically agree with previous speaker, but I will do it a little bit 
in other way ;)

%
% store all necessary data to pass between validators in
% #validator_state struct
%
run_checks(FunList, Data) ->
	run_checks(FunList, Data, #validator_state{}).

%
% evaluate validation chain
%
run_checks([], Data, State) ->
	{valid, Data, State};
run_checks([F | FunList], Data, State) ->
	case ?MODULE:F(Data, State) of
	{valid, NewData, NewState} ->
		run_checks(FunList, NewData, NewState);
	Error = {error, _ErrorMsg, _NewState} ->
		% you can add to error reporting in WHICH
		% validator it failed
		% like
		% {error, F, _ErrorMsg, _NewState}
		Error
	% for ease of programming validators I would add also
	% shortcut return codes like
	% valid -> run_checks(FunList, Data, State);
	% and
         % {error, Error} -> {error, Error, State};
	end.

And I will end up with functions like

% check functions
check_email(PropList, State) ->
	do something and return values ....
check_name(PropList, State) ->
	do something and return values ....


and checking will be as easy as

% InputData is better to be a proplist - much more easier to work with
% or it may be a record of some type
% just tuple will reduce code flexibility in a long run
run_checks([check_email, check_name, check_valid_password], InputData)

So changing order of validation or adding new validator is as easy as 
just writing one more functions and adding it to run_checks call.

And it's also very readable - looking on run_checks invocation you can
easily determine which checks are run and in which order.

I do not hesitate to make check_* functions global - because this will 
also ease writing tests for them (if you use test driver environment).


Disclaimer - I wrote this code out of my head and do not test it, thou 
it should compile with minimal changes, I think :)

/Gaspar

PS. BTW with minimal changes this approach also solves a problem with
destructive variable update, which generated great flame here in a last 
days.

Just extend Data variable to something flexible (in my case I user FIFO 
stack there) and you will get powerful interpretator of "meta commands" 
(like check_email, check_name and so on).


Adam Kelly wrote:
> 2009/3/20 ryeguy <ryeguy1@REDACTED>:
>> 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've added this function to my libraries:
> 
> run_checks([]) -> ok;
> run_checks([H|T]) when is_function(H, 0) ->
> 	case H() of
> 		ok    -> run_checks(T);
> 		Error -> Error
> 	end.
> 
> at which point the problem is simply
> 
> case run_checks([
>               fun() ->check_email(Email) end,
>               fun() ->check_username(Username) end,
>               ... ]) of
>       ok -> ...;
>       {error, Reason} -> ...
> end.
> 
> Adam.

-- 
Gaspar Chilingarov

tel +37493 419763 (mobile - leave voice mail message)
icq 63174784
skype://gasparch
e mailto:nm@REDACTED mailto:gasparch@REDACTED
w http://gasparchilingarov.com/



More information about the erlang-questions mailing list