Example(blang)

Example from RealWorldOCaml

type rec expr<'a> =
  | Base('a)
  | Const(bool)
  | And(list<expr<'a>>)
  | Or(list<expr<'a>>)
  | Not(expr<'a>)

let and_ = l => {
    if Belt.List.some(l, (x) => switch x { 
        | Const(false) => true 
        | _ => false }) { 
        Const(false) 
    }
    else {
        switch Belt.List.keep(l, x => switch x { 
            | Const(true) => false 
            | _ => true}) {
                | list{} => Const(true)
                | list{x} => x
                | l => And(l)
        }
    }
}

let or_ = l => {
   open Belt
   if List.some(l, x => switch x { | Const(true) => true | _ => false}) { Const(true) }
   else {
       switch List.keep(l, x => switch x { | Const(false) => false | _ => true }) {
           | list{} => Const(false)
           | list{x} => x
           | l => Or(l)
       }
   }
}

let not_ = (expr) => {
    switch expr {
        | Const(x) => Const(!x)
        | e => Not(e)
    }
}

let rec simplify = (expr) => {
    open Belt
    switch expr {
        | (Base(_) | Const(_)) as x => x
        | And(l) => and_ (List.map(l, x => simplify(x)))
        | Or(l) => or_ (List.map(l, x => simplify(x)))
        | Not(e) => not_ (simplify(e))
    }
}

let rec eval = (expr, base_eval) => {
  let eval' = expr => eval(expr, base_eval)
  switch expr {
  | Base(base) => base_eval(base)
  | Const(b) => b
  | And(exprs) => Belt.List.every(exprs, eval')
  | Or(exprs) => Belt.List.some(exprs, eval')
  | Not(expr) => !eval'(expr)
  }
}

type mail_field = To | From | CC | Date | Subject
type mail_predicate = {field: mail_field, contains: string}
let test = (field, contains) => Base({field, contains})

let base_eval = ({field, contains}) => {
  switch field {
  | To => contains == "Ryan"
  | Subject => contains == "Maths"
  | _ => true
  }
}

let expr = And(list{
        Or(list{test(To, "Ryan"), test(Subject, "Maths"), Const(false)}), 
        Const(true), 
        Not(Const(true))
    })

let result = eval(simplify(expr), base_eval)
Js.log(result) // false