Created
August 3, 2015 19:15
-
-
Save dvliman/573c5a57267f41d90b3f to your computer and use it in GitHub Desktop.
functional approach in validation. erlang.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(validation_utils). | |
-export([validate_until_first_error/2]). | |
-export([validate_all_errors/2]). | |
-type validation_args() :: [term()]. | |
-type validation_funs() :: [{function(), atom()}]. | |
-spec validate_until_first_error(validation_args(), validation_funs()) -> ok | {error, atom()}. | |
% validate_until_first_error/2 applies list of validation_funs() to each element in args | |
% until it stops at the first error it encounters. There is no restriction | |
% as it what goes to args i.e args can be [term()], [[{key, value}]], etc | |
% and/or what validation_funs() does, as long as it is a predicate that | |
% returns a true or false value. For example: | |
% | |
% say we have these 3 validation functions: | |
% validate_smaller(Left, Right) -> Left < Right. | |
% validate_equals(Left, Right) -> Left =:= Right. | |
% validate_larger(Left, Right) -> Left > Right. | |
% | |
% Args = [1,2] | |
% ValidationFuns = [{fun validate_smaller/2, must_be_smaller}, | |
% {fun validate_larger/2, must_be_larger}, | |
% {fun validate_equals/2, must_be_equal}], | |
% | |
% ValidationFuns are executed in order. Since we are failing on | |
% 'validate_larger', the rest of validation functions are simply | |
% returned and not evaluated from lists:dropwhile. This approach | |
% is pure with high order function and does not wreck havoc the | |
% control flow with throw/catch | |
% | |
% {error, must_be_larger} = validate_until_first_error(Args, ValidationFuns) | |
validate_until_first_error(Args, ValidationFuns) -> | |
case lists:dropwhile(fun validate/1, [{Fn, ErrReason, Args} || {Fn, ErrReason} <- ValidationFuns]) of | |
[] -> ok; | |
[{_Fn, ErrReason, _Args} | _UnexecutedValidationFuns] -> {error, ErrReason} | |
end. | |
-spec validate_all_errors(validation_args(), validation_funs()) -> ok | [{error, atom()}]. | |
% validate_all_errors/2 runs through all list of validation_funs() | |
% and returns either ok or [{error, Reason}, ...] | |
% | |
% we can use validate_all to carry new state through the accumulator | |
validate_all_errors(Args, ValidationFuns) -> | |
case lists:foldl(fun validate/2, [], [{Fn, ErrReason, Args} || {Fn, ErrReason} <- ValidationFuns]) of | |
[] -> ok; | |
ErrorList -> lists:reverse(ErrorList) | |
end. | |
validate({Fn, _ErrReason, Args}) -> | |
apply(Fn, Args). | |
validate({Fn, ErrReason, Args}, ErrorList) -> | |
case apply(Fn, Args) of | |
true -> ErrorList; | |
false -> [{error, ErrReason} | ErrorList] | |
end. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment