Skip to content

Speculative Package Scopes

enumeratio edited this page Oct 1, 2026 · 4 revisions

Packages installed, enabled per scope

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.

1. Installed and enabled

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_1 is 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).

2. Why: notation is local

The conflicts are real and scoped:

  • the hypercomplex unit families (i_k, j_k, ε_k, e_k) against compute-engine's i and e;
  • a Gaussian i against a quaternion i (Algebras, Units and Places §5: names bound per context);
  • shorthands like F_n or μ(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.

3. What compute-engine allows

compute-engine's scopes are evaluation scopes, and its LaTeX dictionary is fixed when an engine is built. Scoped enabling therefore needs one of:

  1. A kernel per enabled set: a custom kernel (Kernels §3) for each distinct set. Correct, but costs an engine per combination.
  2. 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 to e_1). Output is easier: makeBoxes rules consult the scope.
  3. 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.

4. First case: hypercomplex units

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:

  1. 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.
  2. The shorthand as the package's notation: with hypercomplex enabled in a scope, i_1 parses to Unit(Multicomplex, 1) and prints back. Disabled, it's a plain symbol.
  3. A second number system with clashing letters (Gaussian integers' i), to prove the switch both ways.

5. Units

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.

Open

  • 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's Needs("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.

Home

Project

Design: every design page, settled, speculative and archived

Clone this wiki locally