Skip to content

Instantly share code, notes, and snippets.

@mlms13
Created March 28, 2019 23:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mlms13/97dc3e818a8f6da633f380c4c9a3dedc to your computer and use it in GitHub Desktop.
Save mlms13/97dc3e818a8f6da633f380c4c9a3dedc to your computer and use it in GitHub Desktop.
Evaluate rules using other rules?
// I'm not entirely sure what this thing is. It feels powerful and expressive
// and like you can compose things. Not sure what it would be useful for, though.
// Maybe you could make a UI to build arbitrarily complex predicate functions
// for filtering collections of data?
module RuleEngine = {
type t('a) =
| Rule('a)
| Not(t('a))
| And(t('a), t('a))
| Or(t('a), t('a));
let rule = v => Rule(v);
let not_ = v => Not(v);
let and_ = (a, b) => And(a, b);
let or_ = (a, b) => Or(a, b);
let rec evaluate = (evalOne, rule, inpt): bool =>
switch (rule) {
| Rule(v) => evalOne(inpt, v)
| Not(v) => !evaluate(evalOne, v, inpt);
| And(a, b) => evaluate(evalOne, a, inpt) && evaluate(evalOne, b, inpt)
| Or(a, b) => evaluate(evalOne, a, inpt) || evaluate(evalOne, b, inpt)
};
};
module IntRule = {
type t =
| EqualTo(int)
| LessThan(int);
let evalOne = inpt =>
fun
| EqualTo(other) => inpt == other
| LessThan(other) => inpt < other;
let eq = i => RuleEngine.Rule(EqualTo(i));
let lt = i => RuleEngine.Rule(LessThan(i));
let lte = i => RuleEngine.(Or(eq(i), lt(i)));
let gt = i => RuleEngine.(Not(lte(i)));
let gte = i => RuleEngine.(Or(eq(i), gt(i)));
let evaluate = RuleEngine.evaluate(evalOne);
};
module StringRule = {
type t =
| Contains(string)
| StartsWith(string)
| Length(RuleEngine.t(IntRule.t));
let evalOne = inpt =>
fun
| Contains(other) => Js.String.indexOf(other, inpt) >= 0
| StartsWith(other) => Js.String.indexOf(other, inpt) == 0
| Length(rule) => Js.String.length(inpt) |> IntRule.evaluate(rule);
let contains = str => RuleEngine.rule(Contains(str));
let startsWith = str => RuleEngine.rule(StartsWith(str));
let length = rule => RuleEngine.rule(Length(rule));
let evaluate = RuleEngine.evaluate(evalOne);
};
// sugar
let (<&>) = RuleEngine.and_;
let (<|>) = RuleEngine.or_;
// The string that we're evaluating must:
// - Start with "a", OR
// - Have a length >= 4, OR
// - Contain "b" AND contain "c" AND start with "d"
let expr =
StringRule.(
startsWith("a")
<|> length(IntRule.gte(4))
<|> (contains("b") <&> contains("c") <&> startsWith("d"))
);
Js.log(StringRule.evaluate(expr, "bcab"));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment