Module Knowledge.Rule

Rules

Rules are used for introspection and documentation. A rule is a specification of a promise and consists of the following parts

Examples

Suppose, we have a system that describes personal information,

type person
type address

val first_name : (person, string option) slot
val last_name : (person, string option) slot
val full_name : (person, string option) slot

val address : (person, address option) slot
val phone : (person, phone option) slot

The simplest and obvious rule would a derivation of the full name from the first and last names. This derivation could be encoded as a promise. To make this promise visible and known to the users of the knowledge system we advertise it as a rule,

Rule.(declare "full-name" |>
      require first_name  |>
      require last_name   |>
      provide full_name   |>
      comment "full = first last");
promise full_name @@ fun person ->
collect first_name >>=? fun first ->
collect last_name >>|? fun last ->
first ^ " " ^ last

The rule above is a closed term, since it doesn't reference any language variables, i.e., it derives its output directly from the knowledge base. Such rules are called static as they are always available. Contrary, dynamic rules are provide external information to the knowledge system. For example, the following rule extracts the phone number of a person from the online database,

let provide_phone_base =
  Rule.(declare "lookup-phone-number" |>
        dynamic ["phone-base"] |>
        require full_name |>
        provide phone |>
        comment "lookups phone in the public database");
  fun base ->
    promise phone @@ fun person ->
    collect full_name >>| fun name ->
    Phonebase.lookup base phone

The dynamic operator of the specification accepts a list of formal parameters of the rule. In theory, this list could be empty. In that case, the rule will still be treated as dynamic.

Note, that in the example above, the rule declaration is not parametrized with the base. Otherwise, it will be only declared after the base is available. Moreover, each new application will create a new rule, which will end up in a runtime failure, since the rule name should be unique.

This module defines an domain-specific language for specifying rules. The Documentation and Documentation.Rule modules could be used to introspect all available rules.

type def

a term of type def expects either

  • |> require <slot>, or
  • |> provide <slot>.
type doc

a term of type doc expects |> comment <doc>. This is the only way to finish a rule definition.

val declare : ?package:string -> string -> def

declare ?package name starts a rule definition.

The rule definition should be followed by zero or more dependencies,

  • |> require <slot>, zero or more dynamic parameters specifications,
  • |> dynamic <parameters>, exactly one output specification,
  • |> provide <slot>, and exactly one documentation string,
  • |> comment <comment>

Example

declare "rule-name"   |>
dynamic ["parameter"] |>
require slot1         |>
require slot2         |>
provide slot3         |>
comment "example rule"
val dynamic : string list -> def -> def

dynamic ps declares that a rule is dynamic and lists formal parameters.

val require : (_, _) slot -> def -> def

require p declares a dependency on the property p.

Note: This function enables introspection of the rules that are registered in the knowledge base. It doesn't affect the semantics in any way.

val provide : (_, _) slot -> def -> doc

provide p declares that a rule computes the property p

val comment : string -> doc -> unit

comment text provides a documentation for a rule.