Module Blang

Boolean expressions.

A blang is a boolean expression built up by applying the usual boolean operations to properties that evaluate to true or false in some context.

Usage

For example, imagine writing a config file for an application that filters a stream of integers. Your goal is to keep only those integers that are multiples of either -3 or 5. Using Blang for this task, the code might look like:

  module Property = struct
    type t =
      | Multiple_of of int
      | Positive
      | Negative
    [@@deriving sexp]

    let eval t num =
      match t with
      | Multiple_of n -> num % n = 0
      | Positive      -> num > 0
      | Negative      -> num < 0
  end

  type config = {
    keep : Property.t Blang.t;
  } [@@deriving sexp]

  let config = {
    keep =
      Blang.t_of_sexp
        Property.t_of_sexp
        (Sexp.of_string
           "(or (and negative (multiple_of 3)) (and positive (multiple_of 5)))";
  }

  let keep config num : bool =
    Blang.eval config.keep (fun p -> Property.eval p num)

Note how positive and negative and multiple_of become operators in a small, newly-defined boolean expression language that allows you to write statements like (and negative (multiple_of 3)).

Blang sexp syntax

The blang sexp syntax is almost exactly the derived one, except that:

1. Base properties are not marked explicitly. Thus, if your base property type has elements FOO, BAR, etc., then you could write the following Blang s-expressions:

    FOO
    (and FOO BAR)
    (if FOO BAR BAZ)

and so on. Note that this gets in the way of using the blang "keywords" in your value language.

2. And and Or take a variable number of arguments, so that one can (and probably should) write

(and FOO BAR BAZ QUX)

instead of

(and FOO (and BAR (and BAZ QUX)))

If you want to see the derived sexp, use Raw.sexp_of_t.

type +'a t = private
  1. | True
  2. | False
  3. | And of 'a t * 'a t
  4. | Or of 'a t * 'a t
  5. | Not of 'a t
  6. | If of 'a t * 'a t * 'a t
  7. | Base of 'a

Note that the sexps are not directly inferred from the type below -- there are lots of fancy shortcuts. Also, the sexps for 'a must not look anything like blang sexps. Otherwise t_of_sexp will fail. The directly inferred sexps are available via Raw.sexp_of_t.

val compare : ('a -> 'a -> int) -> 'a t -> 'a t -> int
val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> bool
include Sexplib0.Sexpable.S1 with type 'a t := 'a t
val t_of_sexp : (Sexplib0__.Sexp.t -> 'a) -> Sexplib0__.Sexp.t -> 'a t
val sexp_of_t : ('a -> Sexplib0__.Sexp.t) -> 'a t -> Sexplib0__.Sexp.t

Smart constructors that simplify away constants whenever possible

module type Constructors = sig ... end
include Constructors
val base : 'a -> 'a t
val true_ : _ t
val false_ : _ t
val constant : bool -> _ t

function true -> true_ | false -> false_

val not_ : 'a t -> 'a t
val and_ : 'a t list -> 'a t

n-ary And

val or_ : 'a t list -> 'a t

n-ary Or

val if_ : 'a t -> 'a t -> 'a t -> 'a t

if_ if then else

module O : sig ... end
val eval : 'a t -> ('a -> bool) -> bool

eval t f evaluates the proposition t relative to an environment f that assigns truth values to base propositions.