Keeping a scratch namespace pointed at the live world across host reloads (§4).
A reloader that unloads with remove-ns — clj-reload, tools.namespace —
loads the file back into a new namespace object: new vars, and for a
deftype or defrecord, a new class. The host's namespaces are reloaded
with it, dependents included. A scratch is not file-backed, so no reloader
touches it: its aliases, refers and imports keep naming the objects the
reload replaced.
Compiled code mostly escapes that — a compiled fn looks its vars and
classes up again by name when its class loads. What uses the stale
objects themselves is everything evaluated without compiling a class:
a top-level def with a simple init, (def x (f)), and a bare class
symbol, (def c T). Macro expansion through a stale alias or refer
always runs the old macro. The old generation's instances then reach
host code that casts to the new class, and fail as
X cannot be cast to X (measured 2026-10-01, Clojure 1.12.4).
Those three mappings are the whole stale surface reachable from the mapping table. A fully qualified symbol resolves through the namespace registry and is never stale. What is out of reach stays as it is: a fn compiled before the reload holds its vars in its own class, and a def holds whatever value it was given.
Keeping a scratch namespace pointed at the live world across host reloads (§4). A reloader that unloads with `remove-ns` — clj-reload, tools.namespace — loads the file back into a *new* namespace object: new vars, and for a `deftype` or `defrecord`, a new class. The host's namespaces are reloaded with it, dependents included. A scratch is not file-backed, so no reloader touches it: its aliases, refers and imports keep naming the objects the reload replaced. Compiled code mostly escapes that — a compiled fn looks its vars and classes up again by name when its class loads. What uses the stale objects themselves is everything evaluated without compiling a class: a top-level `def` with a simple init, `(def x (f))`, and a bare class symbol, `(def c T)`. Macro expansion through a stale alias or refer always runs the old macro. The old generation's instances then reach host code that casts to the new class, and fail as `X cannot be cast to X` (measured 2026-10-01, Clojure 1.12.4). Those three mappings are the whole stale surface reachable from the mapping table. A fully qualified symbol resolves through the namespace registry and is never stale. What is out of reach stays as it is: a fn compiled before the reload holds its vars in its own class, and a def holds whatever value it was given.
(alias! ns sym target)Point ns's alias sym at target, whatever it named before, in one
atomic step: a concurrent eval compiling in ns sees the old alias or
the new one, never none.
Point `ns`'s alias `sym` at `target`, whatever it named before, in one atomic step: a concurrent eval compiling in `ns` sees the old alias or the new one, never none.
(rewire! ns)Re-point ns's aliases, refers and imports that name what a reload has
since replaced. Returns the re-pointed symbols, nil when nothing was
stale, and says so on *err* — the eval's own output, where the caller
learns that defs made before the reload may still hold the old code and
values. Nothing is ever removed: a mapping whose replacement does not
exist yet is left as it is for a later call.
Re-point `ns`'s aliases, refers and imports that name what a reload has since replaced. Returns the re-pointed symbols, nil when nothing was stale, and says so on `*err*` — the eval's own output, where the caller learns that defs made before the reload may still hold the old code and values. Nothing is ever removed: a mapping whose replacement does not exist yet is left as it is for a later call.
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 |