Function¶
A function exposes a CLIPS deffunction that rules may call from
either the LHS (inside a test: escape hatch) or the RHS. Fathom
recognizes two function subtypes: classification (emitted as a
family of rank/below/meets-or-exceeds/within-scope deffunctions driven
by a HierarchyDefinition) and raw (the author-supplied CLIPS
source is emitted verbatim). Temporal operators (changed_within,
count_exceeds, rate_exceeds, last_n, distinct_count,
sequence_detected, schema_frequency_exceeds) are served by
Engine-registered Python externals
under the reserved fathom- prefix, not by FunctionDefinition.
For conceptual context see
Five Primitives; for exposing
Python callables instead of authoring CLIPS, see
Register a Python function.
Top-level fields — FunctionDefinition¶
| Field | Type | Default | Required | Description |
|---|---|---|---|---|
name |
str |
— | yes | CLIPS identifier. Must match ^[A-Za-z_][A-Za-z0-9_\-]*$ (validated by _name_must_be_clips_ident). For raw functions this is authoring metadata only — the emitted CLIPS name comes from body. For classification the hierarchy name drives the emitted function names (see below). |
description |
str |
"" |
no | Author-facing prose. Not emitted to CLIPS. |
params |
list[str] |
[] |
no | Parameter names. Used when authoring raw bodies; the classification path ignores params and generates its own (?level) / (?a ?b) signatures from the hierarchy. |
hierarchy_ref |
str \| None |
None |
no | Required when type == "classification". Names the hierarchy whose levels drive emission. A trailing .yaml is stripped: hier_name = defn.hierarchy_ref.rsplit(".", 1)[0]. Ignored for raw. |
type |
Literal["classification", "raw"] |
"classification" |
no | Selects the emission strategy. Default is classification. |
body |
str \| None |
None |
no | Raw CLIPS source. Required when type == "raw". Ignored for classification. |
Emission is dispatched in compile_function in
src/fathom/compiler.py. Empty name, missing body for a raw
function, missing hierarchy_ref for a classification function, and
a hierarchy_ref that does not resolve to a registered hierarchy all
raise CompilationError.
Type subtypes¶
classification¶
A classification function expands to a family of deffunctions
named by the hierarchy — not by FunctionDefinition.name. The
authoring name is a handle for the YAML bundle; the emitted CLIPS
identifiers all begin with the hierarchy name (e.g. clearance-rank,
clearance-below). The expansion, from
_compile_classification_functions, is:
(deffunction MAIN::<hier>-rank (?level) (switch (str-cat ?level) (case "LEVEL" then INDEX) … (default (fathom-unknown-level "<hier>" ?level))))— onecaseper level, in declaration order. An out-of-hierarchy input has no rank:fathom-unknown-levelraises, and the evaluation fails withEvaluationError.(deffunction MAIN::<hier>-below (?a ?b) (< (<hier>-rank ?a) (<hier>-rank ?b))).(deffunction MAIN::<hier>-meets-or-exceeds (?a ?b) (>= (<hier>-rank ?a) (<hier>-rank ?b))).(deffunction MAIN::<hier>-within-scope (?a ?b) (and (>= (<hier>-rank ?a) 0) (>= (<hier>-rank ?b) 0)))— true when both inputs are in the hierarchy.
All four rank both sides, so a mis-cased or unknown label on either side stops the evaluation; no rule, allow or deny, decides on it.
The compiler also emits unscoped backward-compat shims —
below, meets-or-exceeds, within-scope — that delegate to the
scoped versions. These shims are emitted only for the first loaded
hierarchy (tracked via self._first_hierarchy_name); subsequent
hierarchies add their scoped deffunctions but do not overwrite the
unscoped shims.
A rule's classification operator does not compile to a shim. It compiles to
the scoped deffunction of the hierarchy that owns the level it names — see
meets_or_exceeds in the rule reference. The
shims exist for a deffunction supplied directly through load_clips_function.
The cross-fact form ($other.level) names no level, so it compiles to the
scoped deffunction of the only hierarchy loaded, and is a CompilationError
when more than one is loaded. Routing it through the first hierarchy's shim
ranked every level of a second hierarchy as unknown.
Compilation failures:
- Missing
hierarchy_ref→CompilationError. hierarchy_refthat names neither a hierarchy in the file'shierarchies:nor a hierarchy file on disk →CompilationError.
raw¶
compile_function returns defn.body as-is. There is no (deffunction
…) wrapping, no parameter binding from params, and no escaping — the
YAML author supplies a complete, parenthesis-balanced CLIPS source
string, which is inserted into the compiled program verbatim.
Compilation failures:
- Missing
body→CompilationError.
Temporal operators (no FunctionDefinition needed)¶
Temporal operators (changed_within, count_exceeds, rate_exceeds,
last_n, distinct_count, sequence_detected,
schema_frequency_exceeds) are served by Engine-registered Python
externals under the reserved fathom- prefix, not by compiled CLIPS
deffunctions. Use them directly in rule
conditions; no FunctionDefinition declaration is required or
accepted — type: temporal was removed in 0.4.0 (raises a Pydantic
validation error if encountered in YAML). See
Register a Python function for
the external-function path and
Not in v1 for the broader roadmap.
HierarchyDefinition fields¶
Classification functions consume a HierarchyDefinition. A function's
hierarchy_ref is looked up first in the hierarchies: list of the same
function file (as in the worked example below). If no hierarchy there has
that name, it is resolved as a file on disk: <ref>, <ref>.yaml or
<ref>.yml, holding a single top-level name: / levels: mapping,
searched next to the function file, then in its hierarchies/
subdirectory, then in a hierarchies/ directory beside it (a pack's
hierarchies/). Every hierarchy in a function file's hierarchies: list
is registered, referenced or not.
| Field | Type | Default | Required | Description |
|---|---|---|---|---|
name |
str |
— | yes | Hierarchy name. Drives the emitted deffunction prefix (<name>-rank, <name>-below, …). |
levels |
list[str] |
— | yes | Ordered lowest-to-highest. rank returns the 0-based index of a level; below / meets-or-exceeds compare ranks; an out-of-hierarchy input fails the evaluation with EvaluationError. |
compartments |
list[str] \| None |
None |
no | Accepted by the model but not consumed by _compile_classification_functions today. Reserved for future compartment-aware emission. |
Worked example — classification¶
YAML input¶
hierarchies:
- name: clearance
levels: [unclassified, confidential, secret, top-secret]
functions:
- name: clearance-check
type: classification
params: [a, b]
hierarchy_ref: clearance
CLIPS output¶
(deffunction MAIN::clearance-rank (?level)
(switch (str-cat ?level)
(case "unclassified" then 0)
(case "confidential" then 1)
(case "secret" then 2)
(case "top-secret" then 3)
(default (fathom-unknown-level "clearance" ?level))))
(deffunction MAIN::clearance-below (?a ?b)
(< (clearance-rank ?a) (clearance-rank ?b)))
(deffunction MAIN::clearance-meets-or-exceeds (?a ?b)
(>= (clearance-rank ?a) (clearance-rank ?b)))
(deffunction MAIN::clearance-within-scope (?a ?b)
(and (>= (clearance-rank ?a) 0) (>= (clearance-rank ?b) 0)))
(deffunction MAIN::below (?a ?b)
(clearance-below ?a ?b))
(deffunction MAIN::meets-or-exceeds (?a ?b)
(clearance-meets-or-exceeds ?a ?b))
(deffunction MAIN::within-scope (?a ?b)
(clearance-within-scope ?a ?b))
Notes:
- The unscoped
below/meets-or-exceeds/within-scopeshims appear becauseclearanceis the first hierarchy loaded. A second hierarchy would emit only its scoped deffunctions; it would not shadow the shims. Rules over the second hierarchy still work: they compile to its scoped deffunctions, chosen by the level they name. FunctionDefinition.name(clearance-check) andparams([a, b]) are not reflected in the output — the classification path drives emission purely from the hierarchy.- Indentation is four spaces; function definitions are joined by blank lines.
Worked example — raw¶
YAML input¶
CLIPS output¶
The body string is emitted exactly — params is authoring metadata
only, and the deffunction's parameter list comes from whatever the
body declares.
Naming convention: the fathom- prefix is reserved for
Engine-registered builtins (fathom-matches, fathom-count-exceeds,
fathom-rate-exceeds, fathom-changed-within, fathom-last-n,
fathom-distinct-count, fathom-sequence-detected,
fathom-parse-compartments, fathom-has-compartment,
fathom-compartments-superset, fathom-dominates, fathom-unknown-level). Python callers of
Engine.register_function are rejected with ValueError if the
requested name starts with fathom-. YAML raw bodies are not
re-checked by the compiler, but avoid the prefix in authored CLIPS to
prevent collisions with the runtime's own bindings.
(fathom-dominates level-a comps-a level-b comps-b hierarchy) raises when
either level is not a level of hierarchy, or the hierarchy is not loaded, so
the evaluation fails with EvaluationError. The Python
fathom.engine.dominates raises EvaluationError in the same cases.
Validators — what is rejected¶
| Condition | Where raised | Error |
|---|---|---|
name that is not a valid CLIPS identifier |
Pydantic validator | ValueError |
Empty name at compile time |
compile_function |
CompilationError |
type: raw with body is None |
compile_function |
CompilationError |
type: classification without hierarchy_ref |
compile_function |
CompilationError |
type: classification with unknown hierarchy_ref |
compile_function |
CompilationError |
Any key not defined on the model, on FunctionDefinition or HierarchyDefinition |
Pydantic (extra="forbid") |
ValidationError |
A top-level key in a function file other than functions and hierarchies |
parse_function_file |
CompilationError |
An invalid or duplicate entry in hierarchies: |
parse_function_file |
CompilationError |
HierarchyDefinition.name that is not a valid CLIPS identifier |
Pydantic validator | ValueError |
A levels or compartments member that is not a valid CLIPS identifier |
Pydantic validator | ValueError |
What is not emitted¶
description— author-facing only.HierarchyDefinition.compartments— accepted by the model, unused by today's classification emission.FunctionDefinition.nameandparams— ignored by theclassificationpath (the hierarchy drives names; parameter signatures are fixed).FunctionDefinition.params— informational forraw(the emitted signature comes frombody).
See also¶
- Five Primitives
- YAML Compilation
- Register a Python function
- Rule reference —
test:escape hatch for calling these functions from the LHS. - Template reference