clara.rules

Forward-chaining rules for Clojure. The primary API is in this namespace.

accumulate

(accumulate & {:keys [initial-value reduce-fn combine-fn retract-fn convert-return-fn], :as args})

DEPRECATED. Use clara.rules.accumulators/accum instead.

Creates a new accumulator based on the given properties:

  • An initial-value to be used with the reduced operations.
  • A reduce-fn that can be used with the Clojure Reducers library to reduce items.
  • A combine-fn that can be used with the Clojure Reducers library to combine reduced items.
  • A retract-fn that can remove a retracted fact from a previously reduced computation
  • An optional convert-return-fn that converts the reduced data into something useful to the caller. Simply uses identity by default.

defquery

macro

(defquery name & body)

Defines a query and stored it in the given var. For instance, a simple query that accepts no parameters would look like this:

(defquery check-job
  "Checks the job for validation errors."
  []
  [?issue <- ValidationError])

See the query authoring documentation for details.

defrule

macro

(defrule name & body)

Defines a rule and stores it in the given var. For instance, a simple rule would look like this:

(defrule hvac-approval
  "HVAC repairs need the appropriate paperwork, so insert
    a validation error if approval is not present."
  [WorkOrder (= type :hvac)]
  [:not [ApprovalForm (= formname "27B-6")]]
  =>
  (insert! (->ValidationError
            :approval
            "HVAC repairs must include a 27B-6 form.")))

See the rule authoring documentation for details.

defsession

macro

(defsession name & sources-and-options)

Creates a sesson given a list of sources and keyword-style options, which are typically Clojure namespaces.

Typical usage would be like this, with a session defined as a var:

(defsession my-session ’example.namespace)

That var contains an immutable session that then can be used as a starting point to create sessions with caller-provided data. Since the session itself is immutable, it can be safely used from multiple threads and will not be modified by callers. So a user might grab it, insert facts, and otherwise use it as follows:

(-> my-session (insert (->Temperature 23)) (fire-rules))

fire-rules

(fire-rules session)

Fires are rules in the given session. Once a rule is fired, it is labeled in a fired state and will not be re-fired unless facts affecting the rule are added or retracted.

This function does not modify the given session to mark rules as fired. Instead, it returns a new session in which the rules are marked as fired.

insert

(insert session & facts)

Inserts one or more facts into a working session. It does not modify the given session, but returns a new session with the facts added.

insert!

(insert! & facts)

To be executed within a rule’s right-hand side, this inserts a new fact or facts into working memory.

Inserted facts are logical, in that if the support for the insertion is removed, the fact will automatically be retracted. For instance, if there is a rule that inserts a “Cold” fact if a “Temperature” fact is below a threshold, and the “Temperature” fact that triggered the rule is retracted, the “Cold” fact the rule inserted is also retracted. This is the underlying truth maintenance facillity.

This truth maintenance is also transitive: if a rule depends on some criteria to fire, and a criterion becomes invalid, it may retract facts that invalidate other rules, which in turn retract their conclusions. This way we can ensure that information inferred by rules is always in a consistent state.

insert-all

(insert-all session fact-seq)

Inserts a sequence of facts into a working session. It does not modify the given session, but returns a new session with the facts added.

insert-all!

(insert-all! facts)

Behaves the same as insert!, but accepts a sequence of facts to be inserted. This can be simpler and more efficient for rules needing to insert multiple facts.

See the doc in insert! for details on insert behavior..

insert-all-unconditional!

(insert-all-unconditional! facts)

Behaves the same as insert-unconditional!, but accepts a sequence of facts to be inserted rather than individual facts.

See the doc in insert-unconditional! for details on uncondotional insert behavior.

insert-unconditional!

(insert-unconditional! & facts)

To be executed within a rule’s right-hand side, this inserts a new fact or facts into working memory.

This differs from insert! in that it is unconditional. The facts inserted will not be retracted even if the rule activation doing the insert becomes false. Most users should prefer the simple insert! function as described above, but this function is available for use cases that don’t wish to use Clara’s truth maintenance.

mk-session

macro

(mk-session & args)

Creates a new session using the given rule sources. The resulting session is immutable, and can be used with insert, retract, fire-rules, and query functions.

If no sources are provided, it will attempt to load rules from the caller’s namespace. This will use rules defined with defrule, queries defined with defquery, and sequences of rule and/or query structures in vars that are annotated with the metadata ^:production-seq.

The caller may also specify keyword-style options at the end of the parameters. Currently four options are supported:

  • :fact-type-fn, which must have a value of a function used to determine the logical type of a given cache. Defaults to Clojure’s type function.
  • :cache, indicating whether the session creation can be cached, effectively memoizing mk-session. Defaults to true. Callers may wish to set this to false when needing to dynamically reload rules.
  • :activation-group-fn, a function applied to production structures and returns the group they should be activated with. It defaults to checking the :salience property, or 0 if none exists.
  • :activation-group-sort-fn, a comparator function used to sort the values returned by the above :activation-group-fn. Defaults to >, so rules with a higher salience are executed first.

This is not supported in ClojureScript, since it requires eval to dynamically build a session. ClojureScript users must use pre-defined rulesessions using defsession.

query

(query session query & params)

Runs the given query with the optional given parameters against the session. The optional parameters should be in map form. For example, a query call might be:

(query session get-by-last-name :last-name “Jones”)

The query itself may be either the var created by a defquery statement, or the actual name of the query.

retract

(retract session & facts)

Retracts a fact from a working session. It does not modify the given session, but returns a new session with the facts retracted.

retract!

(retract! & facts)

To be executed within a rule’s right-hand side, this retracts a fact or facts from the working memory.

Retracting facts from the right-hand side has slightly different semantics than insertion. As described in the insert! documentation, inserts are logical and will automatically be retracted if the rule that inserted them becomes false. This retract! function does not follow the inverse; retracted items are simply removed, and not re-added if the rule that retracted them becomes false.

The reason for this is that retractions remove information from the knowledge base, and doing truth maintenance over retractions would require holding onto all retracted items, which would be an issue in some use cases. This retract! method is included to help with certain use cases, but unless you have a specific need, it is better to simply do inserts on the rule’s right-hand side, and let Clara’s underlying truth maintenance retract inserted items if their support becomes false.