Created
March 28, 2019 23:35
-
-
Save mlms13/97dc3e818a8f6da633f380c4c9a3dedc to your computer and use it in GitHub Desktop.
Evaluate rules using other rules?
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
// 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