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.
  • An optional combine-fn that can be used with the Clojure Reducers library to combine reduced items.
  • An optional 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.

clear-ns-productions!

macro

(clear-ns-productions!)

Ensures that any rule/query definitions which have been cached will be cleared from the associated namespace. Rule and query definitions can be cached such that if their definitions are not explicitly overwritten with the same name (i.e. deleted or renamed), the stale definitions can be loaded into a session using that namespace on reload via the REPL or mechanism such as figwheel. Place (clear-ns-productions!) at the top of any namespace defining rules/queries to ensure the cache is cleared properly.

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)(fire-rules session opts)

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.

This take an additional map of options as a second argument. Current options:

:cancelling true (EXPERIMENTAL, subject to change/removal. Not supported in ClojureScript.):
Simultaneously propagate insertions and retractions through the rules network, at every step using the insertion and retractions of equals facts to cancel each other out and avoid operations deeper in the rules network. The behavior of unconditional insertions and RHS (right-hand side) retractions is undefined when this option is enabled and this option should not be used when calling fire-rules can result in these operations. Note that this is purely a performance optimization and no guarantees are made at this time on whether a given rule’s RHS will be called. When this option is used rule RHS code that is executed shouldn’t do anything that impacts state other than perform logical insertions.

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, which is determined by reading Clojure’s ns var.

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 five options are supported, although most users will either not need these or just the first two:

  • :fact-type-fn, which must have a value of a function used to determine the logical type of a given fact. 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.
  • :ancestors-fn, which returns a collection of ancestors for a given type. Defaults to Clojure’s ancestors function. A fact of a given type will match any rule that uses one of that type’s ancestors. Note that if the collection is ordered this ordering will be maintained by Clara; ordering the ancestors consistently will increase the consistency of overall performance.
  • :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.
  • :forms-per-eval - The maximum number of expressions that will be evaluated per call to eval. Larger batch sizes should see better performance compared to smaller batch sizes. (Only applicable to Clojure) Defaults to 5000, see clara.rules.compiler/forms-per-eval-default for more information.

This is not supported in ClojureScript, since it requires eval to dynamically build a session. ClojureScript users must use pre-defined rule sessions 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.