The source identity: a digest of the engine source that derives belief. A belief image is stamped with it, and an open installs the image only when its own source identity is equal, so an image is never installed by code that would derive a different belief from the same records.
The digest covers the transitive closure of roots over three kinds of edge, each read
from the source files themselves rather than from the loaded namespaces:
ns form's :require, :use and :import clauses — an imported deftype or
defrecord class names the namespace that defines it;quote in code. These are the edges no ns
form states: the requiring-resolve targets and the fixed symbol tables that name
them (vaelii.core's calculi, reasoners and solvers, vaelii.impl.imperative's do/
handlers, vaelii.impl.wiring's three entry points).The walk follows vaelii.core and vaelii.impl.* and nothing else. Code from outside
those prefixes — a registered prover or evaluatable, a foreign plugin — is not in the
digest; vaelii.impl.belief-image refuses to write or install an image for a KB that
runs any.
Each file is read as forms, and the forms are hashed with four things removed: comments,
(comment …) blocks, docstrings, and the reader's position metadata. An edit that
changes only prose therefore leaves the digest unchanged. An edit to code, or to
metadata the compiler reads (^:dynamic, ^:const, a type hint), changes it.
The reader mints a fresh name on every read for two kinds of symbol: a syntax-quote
auto-gensym (x#) and an anonymous-fn argument (%). Both are renumbered in order of
first appearance within their top-level form, so two reads of one file hash alike.
Aliases, classes and vars are read through a *reader-resolver* that resolves each to
itself, so a file reads the same way whether or not its namespace is loaded.
The Clojars jar, the uberjar and a source checkout all carry the .clj files, so every
namespace is normally read as forms. A namespace on the classpath only as compiled
classes contributes the digest of the code it loads from instead: the whole jar its
__init class sits in, or every class file of the namespace in a class directory. A
namespace compiles to one class per fn, so no single class file covers its code. The
jar digest changes with any rebuild, which is conservative: an image is discarded more
often, never installed under different code.
Each third-party library the closure requires or imports contributes the file name of
the jar its code loads from (nippy-3.5.0.jar), and the file name carries the version.
A library loaded from a directory rather than a jar contributes its namespace or class
name. JDK classes contribute nothing.
Nothing here memoizes: a REPL can change a source file between two calls, and the digest describes the files as they stand at the call.
The **source identity**: a digest of the engine source that derives belief. A belief image is stamped with it, and an open installs the image only when its own source identity is equal, so an image is never installed by code that would derive a different belief from the same records. ## Which files The digest covers the transitive closure of `roots` over three kinds of edge, each read from the source files themselves rather than from the loaded namespaces: - the `ns` form's `:require`, `:use` and `:import` clauses — an imported deftype or defrecord class names the namespace that defines it; - every fully-qualified symbol under a `quote` in code. These are the edges no `ns` form states: the `requiring-resolve` targets and the fixed symbol tables that name them (`vaelii.core`'s calculi, reasoners and solvers, `vaelii.impl.imperative`'s `do/` handlers, `vaelii.impl.wiring`'s three entry points). The walk follows `vaelii.core` and `vaelii.impl.*` and nothing else. Code from outside those prefixes — a registered prover or evaluatable, a foreign plugin — is not in the digest; `vaelii.impl.belief-image` refuses to write or install an image for a KB that runs any. ## What is hashed Each file is read as forms, and the forms are hashed with four things removed: comments, `(comment …)` blocks, docstrings, and the reader's position metadata. An edit that changes only prose therefore leaves the digest unchanged. An edit to code, or to metadata the compiler reads (`^:dynamic`, `^:const`, a type hint), changes it. The reader mints a fresh name on every read for two kinds of symbol: a syntax-quote auto-gensym (`x#`) and an anonymous-fn argument (`%`). Both are renumbered in order of first appearance within their top-level form, so two reads of one file hash alike. Aliases, classes and vars are read through a `*reader-resolver*` that resolves each to itself, so a file reads the same way whether or not its namespace is loaded. ## A namespace with no source The Clojars jar, the uberjar and a source checkout all carry the `.clj` files, so every namespace is normally read as forms. A namespace on the classpath only as compiled classes contributes the digest of the code it loads from instead: the whole jar its `__init` class sits in, or every class file of the namespace in a class directory. A namespace compiles to one class per fn, so no single class file covers its code. The jar digest changes with any rebuild, which is conservative: an image is discarded more often, never installed under different code. ## Libraries Each third-party library the closure requires or imports contributes the file name of the jar its code loads from (`nippy-3.5.0.jar`), and the file name carries the version. A library loaded from a directory rather than a jar contributes its namespace or class name. JDK classes contribute nothing. Nothing here memoizes: a REPL can change a source file between two calls, and the digest describes the files as they stand at the call.
(closure)The sorted set of namespaces whose source the digest covers.
The sorted set of namespaces whose source the digest covers.
(forms-digest text)The hex digest of the source text with its comments, docstrings and reader positions
removed.
The hex digest of the source `text` with its comments, docstrings and reader positions removed.
(read-forms text)The top-level forms of the source text, without (comment …) blocks. Reads with
*read-eval* off and the self-resolving *reader-resolver*.
The top-level forms of the source `text`, without `(comment …)` blocks. Reads with `*read-eval*` off and the self-resolving `*reader-resolver*`.
The namespaces the closure starts from: recovery, which derives belief on open, and the public API, whose write entry points derive it between opens.
The namespaces the closure starts from: recovery, which derives belief on open, and the public API, whose write entry points derive it between opens.
(source-identity){:digest hex :namespaces n :libraries [jar …]} for the source on the classpath now.
:digest covers the forms of every namespace in closure, each under its name, and the
sorted library names.
`{:digest hex :namespaces n :libraries [jar …]}` for the source on the classpath now.
`:digest` covers the forms of every namespace in `closure`, each under its name, and the
sorted library names.cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |