Skip to content
zhaber edited this page Feb 24, 2013 · 7 revisions

An input value frequently requires the application of more than one validator or converter. For example, you might want to check whether or not a string in a text box is a valid date, and if it is, whether it comes before some deadline. Composite rules provide a simple function chaining mechanism by which multiple functions may be applied to a single datum in a defined order. If an input value does not meet requirements, the system provides information about which part of the rule it did not pass. For example, if a chosen date does not satisfy one or both of the requirements, the system will let the client know which of them it fails to meet.

Validators can be combined via conjunction, disjunction, and negation operators (&&, ||, !). Rule evaluation is done in compliance with the precedence of logical operators in propositional logic — conjunction has higher precedence than disjunction. Also, similarly to Java logical AND and OR operators, grules uses short-circuit evaluation. Further, we will call a rule built only from validators and logical operators a validation rule.

The chain operator (>>) is similar to a Unix pipeline and divides a rule into independent parts, where each subrule can be either a single converter or a validation rule. Subrules are applied from left to right and if one of them fails, the rest are not checked.

Let’s look at some examples. In each case we will explain how a parameter value is normalized and validated by the rule engine.

id trim >> stripTags

This rule is defined as composition of two subrules and says that a value of the id parameter at first must be normalized by the trim function and then the result must be passed to the second subrule, the stripTags converter. After that the main application is guaranteed to use a clean value of the id parameter.

login trim >> !isAdmin >> stripTags

Here we have three subrules: trim, !isAdmin, and stripTags, so the result of trim application is checked against the !isAdmin validator upon which the value is passed to the stripTags converter.

login isAlnum && (isLengthMore(6) || eq(‘admin’)) >> toLowerCase

There are two subrules in this rule: isAlnum && (isLengthMore(6) || eq(‘admin’)) and toLowerCase. Here, the parameter value is checked against the isAlnum validator and the isLengthMore(5) || eq(‘admin’) validation rule. If both validation rules succeed, the value is transformed to lowercase by the corresponding converter.

Such a rule syntax eliminates syntactic noise that is present in many imperative programming languages and uses a straight sequence for function applications.

One may note analogy with monads in our rule semantics, and indeed the chaining (>>) is a bind operation which takes a parameter value and function that encapsulates preprocessing logic and passes the result of the validator or converter application to the next function or constructs a validation error. We can prove that subrules satisfy three monad axioms:

Right unit:

f >> {return it} ≡ f

Left unit:

{return it} >> f ≡ {f it}

Associative:

m >> f >> g ≡ m >> {g(f it)}