Skip to content
Ryan Brush edited this page Sep 14, 2013 · 28 revisions

Developer documentation is in progress.

Defining rules

Rules are typically defined with defrule, which has this structure:

defrule railroad diagram

A simple rule looks like this:

(defrule free-lunch-with-gizmo
  "Anyone who purchases a gizmo gets a free lunch."
  [Purchase (= item :gizmo)]
  =>
  (insert! (->Promotion :free-lunch-with-gizmo :lunch)))

Where Purchase is a Clojure Record or Java JavaBean containing an item field. If there exists a purchase that matches the :gizmo keyword, a new promotion is inserted into working memory, by simply creating a new Promotion record.

The left-hand side of the rule -- everything prior to the "=>" in the above -- uses a constraint expression described in the Constraint Expressions section below.

The right-hand side of the rule -- everything past the "=>" in the above example -- is simply a Clojure S-expression and can invoke arbitrary code, or use insert! to insert new information into the working memory.

Defining queries

Queries are typically defined with defquery, which has the following structure:

defquery railroad diagram

A sample query looks like this:

(defquery get-promotions
  "Query to find promotions for the purchase."
  []
  [?promotion <- Promotion])

The first argument is a vector of keywords to indicate parameters to a query. For instance, if we wanted to run a query that retrieves only a certain type of promotions, we might write this:

(defquery get-promotions
  "Query to find promotions for the purchase."
  [:?type]
  [?promotion <- Promotion (== ?type type)]) ; Bind the ?type query to the promotion type.

A caller may then execute that query with arguments. So if we only wanted to find lunch promotions, we might perform the query like this:

(query session get-promotions :?type :lunch)

The conditions used by a query are the same structure as the left-hand side of a rule. See the Condition Expressions section below for usage.

Condition Expressions

Condition Expressions are the contents of the left-hand side of rules, or the constraints used in queries.

This part of rules and queries contains a series of expressions, each of which is one of the following:

  • A fact expression, which selects a fact based on some given criteria.
  • A boolean expression, which is simply a hierarchical and/or/not structure of other expressions.
  • An accumulator, which is mechanism to reason about collections of facts
  • A test, which is an arbitrary Clojure S-expression that can run predicate-style tests on variables bound earlier in the rule or query.

Details on each of these are below.

Variable Bindings

A key part of any rule engine is the ability to bind values to variables and unify variables to ensure all items described in a rule are constant.

In Clara, variable bindings can be performed within fact expressions and accumulators described below. They typically take the form of a bind operation ==, or as an assignment-style operator (<-) to bind an entire fact. See the Fact expressions section below for details.

Bound variables can be referenced in tests and the right-hand side of rules.

Fact Expressions

fact expression

Fact expressions are the simplest and most common form of Clara conditions. They start with an optional binding for the fact, in the form of [?variableName <- MyFactType]. The fact type is then followed by zero or more S-expressions which can either be predicates run against a fact, or bindings (via the == macro), as described above.

A simple fact expression may look like this:

[?person <- Person (= first-name "Alice") (== ?last-name last-name)]

This example does the following:

  • Matches all facts of type Person
  • Eliminates any Person facts that don't have a first-name attribute that is equal to "Alice"
  • Creates a new binding ?last-name, which contains the value of the last-name field in matched Person facts.
  • Creates a new binding ?person, which contains the value of the matched Person object.

Boolean Expressions

boolean expression

Boolean expressions are simply prefix-style boolean operations over fact expressions, or nested boolean expressions. Clara prefers the use of keyword ':and', :or', and ':not' for its boolean expressions to keep clear what expressions are part of a Rete network as opposed to a normal Clojure expression.

An example boolean expression may look like this:

[:or [Customer (= status :vip)]
     [Promotion (= type :discount-month)]]

This will generate a rule that fires if the Customer fact has a vip status, or there is a promotion of type discount month.

:and, :or, and :not operations can be nested as one would expect.

Accumulators

accumulator expression

Accumulators are used to reason over collections of facts, much like how we might use map, reduce and similar operations in Clojure. They are similar to the concept of the same name in Jess and Drools.

See the [Accumulators] page for details on writing accumulators, but here we show an example of using a built-in accumulator:

;; Creates an accumulator that selects the item with the newest timestamp field.
(def newest-temp (acc/max :timestamp :returns-fact true))

(defrule get-current-temperature
  [?current-temp <- newest-temp :from [Temperature (== ?location location)]]
  =>
  ; Do something.
  )

Accumulators may also be used to find minimum, maximum, average values, or do other summarization or selection for a set of matching records.

Tests

test expression

Tests in clara are simple predicates that can be run over variables bound earlier in the rule or query. For example:

(defrule get-friends
   [Person (== ?name1 name) (== ?age1 age)]
   [Person (== ?name2 name) (== ?age2 age)]
   [:test (> ?age1 ?age2)]
   =>
   (println (str ?name1 "is older than" ?name2)))

For reasons described in the architecture overview, the rule constraints themselves support only binding via the '==' operator, not arbitrary tests or comparisons between bound values. However, when such tests are necessary, than can be performed in a 'test' expression as seen here.

Additional Resources

Railroad diagrams generated with http://railroad.my28msec.com/rr/ui

Clone this wiki locally