Rules
Rules are used for introspection and documentation. A rule is a specification of a promise and consists of the following parts
- a unique rule name,
- an optional sequence of external dependencies,
- an optional sequence of inputs,
- the output,
- the documentation string.
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.
a term of type def
expects either
|> require <slot>
, or|> provide <slot>
.
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.
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.
provide p
declares that a rule computes the property p
comment text
provides a documentation for a rule.