Liking cljdoc? Tell your friends :D

vaelii.impl.io.text

The text KB format — one file per context, one s-expression per sentence — read and written.

It is the format the shipped ontology is authored in (resources/kb/, vaelii.host.seed), and this is where the writer for it lives, so a KB an author edited as text can be got back out of a store as text. The three formats a KB moves in are different questions and stay separate entry points:

what it holdswho reads it
text (here)premises and inert sentexes, in the author's own spelling, no handlesassert, assert-inert
export dump (io.export / io.import)every record and justification at its own handleimport!
the store itselfthe live KBthe engine

A dump is a KB's state; text is a KB's content. Only the second survives a re-derivation, a rename or an engine that concludes something new — which is what an author editing an ontology wants, and what a dump deliberately is not.

The format

Every form is one sentence, read with clojure.edn, so a KB file is data and can never run code. ;; comments and blank lines are free — the reader is the EDN reader, so it skips them without a line-oriented pass. The file name is the context: CxKinship.txt asserts into CxKinship, and a sentence for another context says so with (ist Cx S) as it would anywhere else. A rule carries its set/*Rule / set/defaultRule wrappers and its exceptWhen exactly as an author writes them, because that is what assert reads.

Two wrappers are not part of the sentence, because each states how the sentence is written rather than what it says: (set/monotonic S) asserts S known-true, and (set/inert S) stores S through assert-inert. peel-form takes either off a whole form, and neither reaches the store as a functor. :default is the entry point's own fallback and is written as nothing.

An exceptWhen states a strength per half, because it asserts two things: the rule, and the exception qualifying it. The outer wrapper is the assertion's own option and assert gives it to both, so it can only state a class the two share; a wrapper on the query states the exception's own, and assert reads that one itself (sentex/peel-exception-strength), so a hand-written KB spells it the same way. Four pairings, four spellings:

ruleexceptionwritten as
defaultdefault(exceptWhen Q R)
defaultknown-true(exceptWhen (set/monotonic Q) R)
known-truedefault(exceptWhen Q R), and (set/monotonic R) on a line of its own
known-trueknown-true(set/monotonic (exceptWhen Q R))

The third takes two lines because there is no wrapper for weakening a half: the rule gets a line of its own at the stronger class, and mark-premise resolves a premise asserted twice to the stronger of the two.

What a text export holds, and what it does not

Premises and inert sentexes only. A derived sentex is what the engine concluded from the premises, so writing it out would store as a premise what the KB believes as a conclusion — a reload would hold it against retraction of everything it followed from. Chaining puts it back at load, which is the whole point.

No handles. A text KB is re-asserted rather than restored, so it lands at whatever handles the loading KB mints. A caller who needs handle identity across the round trip wants export!, not this.

Deterministic. Files are named for their contexts and their forms are ordered by content (nm/by-print-key), never by handle — so two KBs holding the same knowledge export byte-identical files whatever order they were built in (docs/defenses.md, "Tie-breaks and orderings key on content").

The **text KB format** — one file per context, one s-expression per sentence — read
and written.

It is the format the shipped ontology is authored in (`resources/kb/`,
`vaelii.host.seed`), and this is where the writer for it lives, so a KB an author
edited as text can be got back out of a store as text.  The three formats a KB moves
in are different questions and stay separate entry points:

| | what it holds | who reads it |
|---|---|---|
| **text** (here) | premises and inert sentexes, in the author's own spelling, no handles | `assert`, `assert-inert` |
| **export dump** (`io.export` / `io.import`) | every record and justification at its own handle | `import!` |
| the **store** itself | the live KB | the engine |

A dump is a KB's *state*; text is a KB's *content*.  Only the second survives a
re-derivation, a rename or an engine that concludes something new — which is what an
author editing an ontology wants, and what a dump deliberately is not.

## The format

Every form is one sentence, read with `clojure.edn`, so a KB file is data and can
never run code.  `;;` comments and blank lines are free — the reader is the EDN
reader, so it skips them without a line-oriented pass.  **The file name is the
context**: `CxKinship.txt` asserts into `CxKinship`, and a sentence for another
context says so with `(ist Cx S)` as it would anywhere else.  A rule carries its
`set/*Rule` / `set/defaultRule` wrappers and its `exceptWhen` exactly as an author
writes them, because that is what `assert` reads.

**Two wrappers are not part of the sentence**, because each states how the sentence is
written rather than what it says: `(set/monotonic S)` asserts `S` known-true, and
`(set/inert S)` stores `S` through `assert-inert`.  `peel-form` takes either off a whole
form, and neither reaches the store as a functor.  `:default` is the entry point's own
fallback and is written as nothing.

**An `exceptWhen` states a strength per half**, because it asserts two things: the rule,
and the exception qualifying it.  The outer wrapper is the assertion's own option and
`assert` gives it to *both*, so it can only state a class the two share; a wrapper on
the **query** states the exception's own, and `assert` reads that one itself
(`sentex/peel-exception-strength`), so a hand-written KB spells it the same way.  Four
pairings, four spellings:

| rule | exception | written as |
|---|---|---|
| default | default | `(exceptWhen Q R)` |
| default | known-true | `(exceptWhen (set/monotonic Q) R)` |
| known-true | default | `(exceptWhen Q R)`, and `(set/monotonic R)` on a line of its own |
| known-true | known-true | `(set/monotonic (exceptWhen Q R))` |

The third takes two lines because there is no wrapper for *weakening* a half: the rule
gets a line of its own at the stronger class, and `mark-premise` resolves a premise
asserted twice to the stronger of the two.

## What a text export holds, and what it does not

**Premises and inert sentexes only.**  A derived sentex is what the engine concluded
from the premises, so writing it out would store as a premise what the KB believes as a
conclusion — a reload would hold it against retraction of everything it followed from.
Chaining puts it back at load, which is the whole point.

**No handles.**  A text KB is re-asserted rather than restored, so it lands at
whatever handles the loading KB mints.  A caller who needs handle identity across the
round trip wants `export!`, not this.

**Deterministic.**  Files are named for their contexts and their forms are ordered by
content (`nm/by-print-key`), never by handle — so two KBs holding the same knowledge
export byte-identical files whatever order they were built in
(docs/defenses.md, "Tie-breaks and orderings key on content").
raw docstring

context-ofclj

(context-of f)

The context a text KB file asserts into: its name without the .txt.

The context a text KB file asserts into: its name without the `.txt`.
sourceraw docstring

entriesclj

(entries source)

Every [form context] a text KB holds, with the files they came from: {:files ["CxUniverse.txt" …] :entries [[form context] …] :path "…"}.

source is a directory of Cx….txt files — an export-text! destination, or an ontology tree an author keeps — or a single such file. Either way the context of a form is its file's name, so a directory is read whole and handed to load-entries! in one pass rather than a file at a time: the names sort alphabetically, which is not a dependency order, and one context's content can rest on another's.

Every `[form context]` a text KB holds, with the files they came from:
`{:files ["CxUniverse.txt" …] :entries [[form context] …] :path "…"}`.

`source` is a **directory** of `Cx….txt` files — an `export-text!` destination, or an
ontology tree an author keeps — or a single such file.  Either way the context of a
form is its file's name, so a directory is read whole and handed to `load-entries!` in
one pass rather than a file at a time: the names sort alphabetically, which is not a
dependency order, and one context's content can rest on another's.
sourceraw docstring

form-spansclj

(form-spans source)

Every form in the text KB file source (anything io/reader takes), in file order, as {:form f :start line :end line}: the 1-based lines the form opens and closes on. clojure.edn, so comments and blank lines added no work and no code can run. A form that does not read is refused as :unreadable, carrying :file and the :line it opens on and never the file's text.

Every form in the text KB file `source` (anything `io/reader` takes), in file order, as
`{:form f :start line :end line}`: the 1-based lines the form opens and closes on.
`clojure.edn`, so comments and blank lines added no work and no code can run.  A form
that does not read is refused as `:unreadable`, carrying `:file` and the `:line` it
opens on and never the file's text.
sourceraw docstring

inert-wrapperclj

(set/inert S) — S stored through assert-inert: indexed and durable, never a premise. Read by the text KB format alone, around a whole form; assert does not read it.

`(set/inert S)` — `S` stored through `assert-inert`: indexed and durable, never a
premise.  Read by the text KB format alone, around a whole form; `assert` does not read
it.
sourceraw docstring

kb-file?clj

(kb-file? f)

Is f a context file of a text KB directory — a regular Cx….txt? The prefix is the discriminant so a directory holding a README or a checksum is read for its KB files rather than refused for the rest.

Is `f` a context file of a text KB directory — a regular `Cx….txt`?  The prefix is
the discriminant so a directory holding a README or a checksum is read for its KB
files rather than refused for the rest.
sourceraw docstring

kb-filesclj

(kb-files dir)

The context files of the text KB directory dir, in name order.

The context files of the text KB directory `dir`, in name order.
sourceraw docstring

load-entries!clj

(load-entries! assert! assert-inert! entries)

Assert every [form context] of entries through assert! — (assert! sentence context opts), or (assert-inert! sentence context) for a form peel-form reads as inert — order-insensitively: an entry refused because content further down the list has not arrived yet is retried rather than fatal. Returns how many entries were read.

Both writers are passed in rather than required: this sits under vaelii.core, and the two callers above it are vaelii.core/load-text! and vaelii.host.seed.

A KB file's order is its terms', not its dependencies' — blocks run in natural sort order, which is the whole point of grouping term-centrically — so a file cannot also be dependency-ordered. (transitiveInArgInverse largerThan 1 partOf) sits under largerThan and (transitive partOf) under partOf, and l sorts before p. Several checks read the store (a preservation's transitivity, an arg clash, a disjointness), so without this a term-centric file is refused for where its author filed a sentence. A whole directory is one pass, for the same reason one level up: the file names sort alphabetically and a context's content can rest on another's. The context topology goes first whatever order it arrived in (topology-first), which is the one thing retrying cannot fix.

Retry rounds run while each one makes progress; the entries that survive a round that changed nothing are re-asserted without a catch, so a genuinely ill-formed one still throws — carrying the error it has once everything that could have helped it is stored, which is the error worth reporting. A list that loads clean pays one try per entry and no second round.

Assert every `[form context]` of `entries` through `assert!` —
`(assert! sentence context opts)`, or `(assert-inert! sentence context)` for a form
`peel-form` reads as inert — **order-insensitively**: an entry refused because
content further down the list has not arrived yet is retried rather than fatal.
Returns how many entries were read.

Both writers are passed in rather than required: this sits under `vaelii.core`, and the
two callers above it are `vaelii.core/load-text!` and `vaelii.host.seed`.

A KB file's order is its *terms'*, not its dependencies' — blocks run in natural sort
order, which is the whole point of grouping term-centrically — so a file cannot also
be dependency-ordered.  `(transitiveInArgInverse largerThan 1 partOf)` sits under `largerThan`
and `(transitive partOf)` under `partOf`, and `l` sorts before `p`.  Several checks
read the store (a preservation's transitivity, an `arg` clash, a disjointness), so
without this a term-centric file is refused for where its author filed a sentence.
**A whole directory is one pass**, for the same reason one level up: the file names
sort alphabetically and a context's content can rest on another's.  The **context
topology goes first** whatever order it arrived in (`topology-first`), which is the one
thing retrying cannot fix.

Retry rounds run while each one makes progress; the entries that survive a round that
changed nothing are re-asserted **without a catch**, so a genuinely ill-formed one
still throws — carrying the error it has once everything that could have helped it is
stored, which is the error worth reporting.  A list that loads clean pays one `try`
per entry and no second round.
sourceraw docstring

peel-formclj

(peel-form form)

[sentence opts] for one form of a text KB file: (set/monotonic S) is S with {:strength :monotonic}, (set/inert S) is S with {:inert true}, which tells the caller to write S through assert-inert, and anything else is itself with nil. assert refuses :inert as an unknown option, so a caller that does not dispatch on it fails at the write.

A malformed wrapper is refused :shape: one wrapping no sentence or two, and one wrapping the other, since an inert sentex has no strength. Passed through, each would reach assert as a sentence whose functor is a namespace-qualified symbol.

`[sentence opts]` for one form of a text KB file: `(set/monotonic S)` is `S` with
`{:strength :monotonic}`, `(set/inert S)` is `S` with `{:inert true}`, which tells the
caller to write `S` through `assert-inert`, and anything else is itself with `nil`.
`assert` refuses `:inert` as an unknown option, so a caller that does not dispatch on
it fails at the write.

A malformed wrapper is refused `:shape`: one wrapping no sentence or two, and one
wrapping the other, since an inert sentex has no strength.  Passed through, each would
reach `assert` as a sentence whose functor is a namespace-qualified symbol.
sourceraw docstring

premise-entriesclj

(premise-entries kb records)

[entries skipped] for records of kb, premises and inert sentexes: each as the form a KB file writes it, {:context c :form f :handles [h …]}, and the records there is no text for. :handles are the records the form stands for — one, or an exceptWhen meta and the rule it qualifies — so a caller that finds a form it does not want knows what to retract.

Three kinds of record, and the second is the one that needs assembling:

  • an ordinary fact or rule is authored under its strength wrapper, and an inert sentex, which has no strength, under (set/inert …);
  • an exceptWhen meta-sentex is not written as stored — (exceptWhen <query> (sentexHandle H)) names a handle the reading KB does not have — but as the (exceptWhen <query> <rule>) wrapper it was asserted as, under whichever strength wrappers reproduce the two halves' classes (exception-form). Its target rule is then not written again on its own, exactly as a shipped file writes the wrapper and not the bare rule; a rule known-true under a :default exception gets its own line as well, since the wrapper would state the weaker class and mark-premise resolves to the stronger of the two.
  • anything else naming a handle — an (except H), a target_following_predicate meta, an exceptWhen meta on a rule the KB derived — is skipped and counted (names-a-handle? for the two ways a sentence spells one). There is no spelling for it: the handle is a fact about this store, and a wrapper around a derived rule would assert as a premise something the KB never held as one.
`[entries skipped]` for `records` of `kb`, premises and inert sentexes: each as the form
a KB file writes it, `{:context c :form f :handles [h …]}`, and the records there is no
text for.  `:handles` are the records the form stands for — one, or an exceptWhen meta
and the rule it qualifies — so a caller that finds a form it does not want knows what to
retract.

Three kinds of record, and the second is the one that needs assembling:

* an ordinary fact or rule is `authored` under its strength wrapper, and an inert
  sentex, which has no strength, under `(set/inert …)`;
* an **exceptWhen meta-sentex** is not written as stored — `(exceptWhen <query>
  (sentexHandle H))` names a handle the reading KB does not have — but as the
  `(exceptWhen <query> <rule>)` wrapper it was asserted as, under whichever strength
  wrappers reproduce the two halves' classes (`exception-form`).  Its target rule is
  then not written again on its own, exactly as a shipped file writes the wrapper and
  not the bare rule; a rule known-true under a `:default` exception gets its own line
  as well, since the wrapper would state the weaker class and `mark-premise` resolves
  to the stronger of the two.
* anything else naming a handle — an `(except H)`, a `target_following_predicate` meta,
  an exceptWhen meta on a rule the KB *derived* — is **skipped and counted**
  (`names-a-handle?` for the two ways a sentence spells one).  There
  is no spelling for it: the handle is a fact about this store, and a wrapper around a
  derived rule would assert as a premise something the KB never held as one.
sourceraw docstring

read-formsclj

(read-forms source)

Every form in the text KB file source, in file order (form-spans).

Every form in the text KB file `source`, in file order (`form-spans`).
sourceraw docstring

write-kb!clj

(write-kb! kb dir)
(write-kb! kb dir o)

Write kb's premises and inert sentexes into dir as a text KB — one <Context>.txt per context — and return a summary:

{:contexts n :sentences n :skipped n :files ["CxUniverse.txt" …]
 :bytes n :elapsed-ms n :dir "…"}

opts narrows what is written: {:context C} for that one context's file, {:ancestor-set C} for C and every context it sees. Neither, and every context with a premise or an inert sentex in it is written. A key this fn does not read is refused (:unknown-option) — a misspelt narrowing writes the whole KB under a summary that looks right.

dir must be absent or empty. See the namespace docstring for what a text KB holds and what it deliberately does not.

Write `kb`'s premises and inert sentexes into `dir` as a text KB — one
`<Context>.txt` per context — and return a summary:

    {:contexts n :sentences n :skipped n :files ["CxUniverse.txt" …]
     :bytes n :elapsed-ms n :dir "…"}

`opts` narrows what is written: `{:context C}` for that one context's file, `{:ancestor-set C}`
for `C` and every context it sees.  Neither, and every context with a premise or an inert
sentex in it is written.  A key this fn does not read is refused (`:unknown-option`) — a misspelt
narrowing writes the whole KB under a summary that looks right.

`dir` must be absent or empty.  See the namespace docstring for what a text KB holds
and what it deliberately does not.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close