Repository navigation
Speculative Package Scopes
Speculative design. Open, not settled — thinking and proposals, not shipped behavior. See Roadmap for what's in flight.
Status: speculative (2026-10-01). A kernel should know about every package it can reach, its heads, notations and metadata, without all of them being in force everywhere. A scope (a notebook, a cell, a page) enables the packages it wants; their names resolve by short name and their notations parse and print there, and nowhere else. Number systems become packages that are switched on and off, and notational conflicts surface when two are switched on together, not when both are installed.
Kernels and Front Ends §2 already gives the host a catalogue of what's available, installed and enabled by default. This page is about the last of those, made per scope:
- Installed: the kernel holds the package's manifest slice, its notation entry point (rules, dictionary entries, macros: Kernels §6) and its metadata. It can say what the package offers, and what enabling it would change. Its definitions load lazily, as now (Lazy Engine §2).
- Enabled in a scope: its heads resolve by short name, its input notation parses, its output notation prints. This is a context on the search path (Namespaces §4), with notation riding along with the names.
-
Installed, not enabled: its heads are reachable qualified, and its notation is inert:
i_1is just a subscripted symbol. A short name only it declares is an error that says which package would enable it, as an unbound registry name already is (Lazy Engine §3).
The conflicts are real and scoped:
- the hypercomplex unit families (
i_k,j_k,ε_k,e_k) against compute-engine'siande; - a Gaussian
iagainst a quaternioni(Algebras, Units and Places §5: names bound per context); - shorthands like
F_norμ(n)that mean different things in different packages (Kernels §6, dressed shorthands).
Each is fine inside one scope and wrong across all of them. Enabling per scope lets a document say which reading it means, and lets the ambiguity checks (Kernels §6: static triggers, round trip, interference) run over the enabled set, at enable time, rather than over everything installed, at startup.
compute-engine's scopes are evaluation scopes, and its LaTeX dictionary is fixed when an engine is built. Scoped enabling therefore needs one of:
- A kernel per enabled set: a custom kernel (Kernels §3) for each distinct set. Correct, but costs an engine per combination.
-
Parse with everything, then filter by scope. Every installed package's entries are in
the dictionary (as Kernels §6 already requires), each tagged with its package. A parse result
from a package the scope hasn't enabled is lowered back to plain symbols (
Unit(Clifford, 1)back toe_1). Output is easier:makeBoxesrules consult the scope. - Upstream: dictionaries per scope. The cleanest, and the ask to pair with notation per library.
(2) is buildable on our side today, with (3) as the ask.
Definitions. A package that only adds rows, heads or read-only data (Kernels §3) can be enabled per scope in a shared engine: enabling changes resolution and notation, not definitions. One that overrides or specialises needs a custom kernel when enabled, as now.
Today the units are subscripted symbols the hypercomplex package declares
(hypercomplex/src/units.ts: i_k, j_k, ε_k, e_k). They're global once declared, which is
the conflict. The path:
-
Units as values:
Unit(family, k), an explicit head (Algebras, Units and Places §1–5). Examples spell units this way, and nothing global is claimed. -
The shorthand as the package's notation: with hypercomplex enabled in a scope,
i_1parses toUnit(Multicomplex, 1)and prints back. Disabled, it's a plain symbol. -
A second number system with clashing letters (Gaussian integers'
i), to prove the switch both ways.
Units are the clearest case of something packages should install rather than the engine holding
globally. compute-engine's physical unit table is global today (2\,\mathrm{N} is newtons
everywhere), and the hypercomplex units are declared globally too (§4). Packages instead
introduce or depend on dimensions, units and unit notation; a scope enables unit notation like
any other (§1, §3), and a set of unit systems over disjoint dimensions. The design, with one
notion of dimension under physical and algebraic units alike, is
Units and Dimensions.
- Granularity. A whole package, or named notation sets within one (a package with a terse and a verbose notation)?
-
How a document says it.
Needs("hypercomplex"), Wolfram-style, beside the registry'sNeeds("registry/Name@1.2"); and a cell-level form. - Defaults. Which packages a host enables everywhere (most of arithmetic, surely), and how a scope disables one of those.
- Printing across scopes. An expression made in one scope and shown in another prints with the viewer's notation, or carries its own?
- Interaction with Foreign Names. Enabling a package widens the set of our globals a foreign variable could be captured by; the inbound rename pass reads the scope's enabled set, not just compute-engine's definitions.