|
| 1 | +<!-- SPDX-License-Identifier: PMPL-1.0-or-later --> |
| 2 | +<!-- Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk> --> |
| 3 | + |
| 4 | +# 2. Reversibility Design as a Case Study in Type-System Power |
| 5 | + |
| 6 | +Date: 2026-04-12 |
| 7 | + |
| 8 | +## Status |
| 9 | + |
| 10 | +Accepted |
| 11 | + |
| 12 | +## Context |
| 13 | + |
| 14 | +In April 2026, the JTV v2 reversibility system for the 007 language underwent |
| 15 | +a comprehensive design session. Five open design decisions were closed, covering: |
| 16 | +variable mutation model, linear handle behaviour, agent state rollback, the |
| 17 | +reversible–reverse pairing mechanism, and branch interaction. |
| 18 | + |
| 19 | +During that session, three occasions arose where a proposed runtime mechanism, |
| 20 | +tooling addition, or syntactic obligation was found to be **entirely redundant** |
| 21 | +because the type system already expressed the same constraint. Each time, |
| 22 | +the mechanism was dropped and the type system did the work alone. |
| 23 | + |
| 24 | +These three collapses are documented here as a TypeLL case study — concrete |
| 25 | +evidence of the principle that a well-designed type system eliminates |
| 26 | +mechanisms rather than multiplying them. |
| 27 | + |
| 28 | +--- |
| 29 | + |
| 30 | +## Decision |
| 31 | + |
| 32 | +Record the three type-system collapses from the 007 JTV v2 reversibility |
| 33 | +design as canonical examples of type-system power, for use in TypeLL |
| 34 | +documentation, teaching material, and type-system design rationale. |
| 35 | + |
| 36 | +--- |
| 37 | + |
| 38 | +## The Three Collapses |
| 39 | + |
| 40 | +### Collapse 1 — `ExternalHandle` type eliminates `unsend` primitive |
| 41 | + |
| 42 | +**Proposed mechanism:** A new `unsend(h, v)` primitive, callable only inside |
| 43 | +`reverse { }` blocks, to undo a `send` on a linear handle. Required a new |
| 44 | +grammar production, a new typechecker rule for reversal-context tracking, |
| 45 | +and a handle state machine (`Ready → Consumed → Ready`). |
| 46 | + |
| 47 | +**The type-system observation:** The handle already carries a type. If the |
| 48 | +handle's type is `ExternalHandle` (crossing an agent boundary), then |
| 49 | +attempting to `send` inside a `reversible { }` block is a **static error** — |
| 50 | +the same way any other type violation is caught. No new primitive. No new |
| 51 | +runtime check. No reversal-context flag in the typechecker. |
| 52 | + |
| 53 | +**What the type does:** The `ExternalHandle` annotation is the enforcement |
| 54 | +mechanism. The type tells you at the call site whether the send is |
| 55 | +reversible-safe. `unsend` becomes unreachable — its only purpose was to |
| 56 | +handle the case the type system now prevents statically. |
| 57 | + |
| 58 | +**Lesson:** Before adding a primitive to handle a dangerous operation, ask |
| 59 | +whether the operation's danger is already expressible in the type system. If |
| 60 | +the type of the target already encodes the relevant constraint, the primitive |
| 61 | +is redundant. |
| 62 | + |
| 63 | +--- |
| 64 | + |
| 65 | +### Collapse 2 — `Option<ReversalToken>` eliminates the asymmetric-branch lint |
| 66 | + |
| 67 | +**Proposed mechanism:** A compiler lint warning: "this `branch` is asymmetric |
| 68 | +— some arms produce a `ReversalToken`, some do not. Did you mean to handle |
| 69 | +both cases?" Required a separate analysis pass, a warning type, and |
| 70 | +documentation of when to suppress it. |
| 71 | + |
| 72 | +**The type-system observation:** At a `branch` join point where some arms |
| 73 | +produce a `ReversalToken` and some do not, path-sensitive typing automatically |
| 74 | +promotes the join type to `Option<ReversalToken<S>>`. The programmer who |
| 75 | +receives `Option<ReversalToken<S>>` in scope *already knows* the branch was |
| 76 | +asymmetric — the type told them. And `match` on `Option<ReversalToken<S>>` |
| 77 | +forces exhaustive handling of `Some(tok)` and `None`. The programmer cannot |
| 78 | +ignore or forget the asymmetric case. |
| 79 | + |
| 80 | +**What the type does:** The `Option`-lifting IS the warning. It is stronger |
| 81 | +than a lint — a lint can be suppressed, ignored, or not seen in CI. An |
| 82 | +`Option` that the programmer must `match` on cannot be silently skipped. |
| 83 | +The type enforces the handling; the lint would merely have suggested it. |
| 84 | + |
| 85 | +**Lesson:** When you find yourself wanting to add a lint for "this pattern |
| 86 | +is probably wrong," ask whether a type transformation at the relevant join |
| 87 | +point would make the pattern impossible to ignore rather than merely |
| 88 | +flagged. Lints are hints; types are guarantees. |
| 89 | + |
| 90 | +--- |
| 91 | + |
| 92 | +### Collapse 3 — `ReversalToken<S>` type parameter eliminates the snapshot data structure for local bindings |
| 93 | + |
| 94 | +**Proposed mechanism:** A `ReversalLog` / snapshot data structure in the |
| 95 | +evaluator — a `HashMap<String, RtValue>` capturing all local bindings at |
| 96 | +`reversible` entry, restored at `reverse`. Required a new struct, insertion |
| 97 | +on every binding within a reversible block, and a restore pass. |
| 98 | + |
| 99 | +**The type-system observation:** In a purely functional language, local |
| 100 | +bindings do not need snapshotting. `let x = x + 3` inside a `reversible` |
| 101 | +block creates a *new* binding that shadows the outer `x = 5`. When the block |
| 102 | +exits, the shadow goes out of scope and the outer binding reappears. Lexical |
| 103 | +scope provides snapshot-and-restore for local bindings automatically and for |
| 104 | +free. The `HashMap` snapshot would have been capturing something the language |
| 105 | +already maintained. |
| 106 | + |
| 107 | +The *only* things that genuinely require snapshotting are `@state` fields — |
| 108 | +because those escape lexical scope, persisting at agent lifetime. And these |
| 109 | +are precisely what the `ReversalToken<S>` type parameter encodes: `S` is |
| 110 | +the record type of the captured `@state` field values. The token IS the |
| 111 | +snapshot. No separate data structure. No general-purpose `HashMap`. The |
| 112 | +type parameter makes it precise, typed, and statically verified. |
| 113 | + |
| 114 | +**What the type does:** `ReversalToken<{ @balance: Int }>` carries exactly |
| 115 | +what was captured, at the type level. The evaluator's restore path unpacks |
| 116 | +the token's payload and rebinds those fields — a typed operation over a |
| 117 | +known structure, not a dynamic HashMap lookup. Decisions 1 (snapshot) and 4 |
| 118 | +(linear token) collapse into a single mechanism: the token IS the snapshot, |
| 119 | +for the only things that actually needed snapshotting. |
| 120 | + |
| 121 | +**Lesson:** Before adding a runtime data structure to track something, ask |
| 122 | +whether the language's existing semantics already maintain it. In a purely |
| 123 | +functional language, immutability and lexical scoping do enormous amounts of |
| 124 | +tracking work invisibly. The snapshot data structure was solving a problem |
| 125 | +that didn't exist in the language as designed. |
| 126 | + |
| 127 | +--- |
| 128 | + |
| 129 | +## The Pattern |
| 130 | + |
| 131 | +All three collapses follow the same shape: |
| 132 | + |
| 133 | +1. A mechanism is proposed to handle a problem. |
| 134 | +2. On closer inspection, the type system already expresses the relevant |
| 135 | + constraint, handles the relevant distinction, or maintains the relevant |
| 136 | + invariant. |
| 137 | +3. The mechanism is dropped. The type system does the work. |
| 138 | + |
| 139 | +The result in each case: **fewer moving parts, stronger guarantees, smaller |
| 140 | +implementation surface.** A lint that can be suppressed becomes a type that |
| 141 | +cannot be ignored. A primitive with a dangerous footgun becomes a type error. |
| 142 | +A runtime data structure becomes a type parameter. |
| 143 | + |
| 144 | +This is the direction a well-typed language should run: each addition to the |
| 145 | +type system should eliminate at least one mechanism elsewhere. If you are |
| 146 | +adding to the type system and also adding runtime machinery for the same |
| 147 | +concern, the design is not finished. |
| 148 | + |
| 149 | +--- |
| 150 | + |
| 151 | +## Consequences |
| 152 | + |
| 153 | +### Positive |
| 154 | + |
| 155 | +- Three concrete, worked examples of the "type system as mechanism eliminator" |
| 156 | + principle, grounded in a real language design session. |
| 157 | +- Usable directly in TypeLL documentation, teaching material on linear types, |
| 158 | + and type-system design rationale documents. |
| 159 | +- Demonstrates that the principle applies across different type-system features: |
| 160 | + type annotations on values (`ExternalHandle`), type algebra at join points |
| 161 | + (`Option`-lifting), and type parameters encoding captured state |
| 162 | + (`ReversalToken<S>`). |
| 163 | +- The three collapses are believed to constitute a novel contribution to the |
| 164 | + reversible computation and type theory literature, publishable as part of |
| 165 | + the 007 / JTV v2 language papers. |
| 166 | + |
| 167 | +### Negative |
| 168 | + |
| 169 | +- The collapses only reveal themselves during design — they require the |
| 170 | + designer to resist the momentum toward adding a mechanism and instead ask |
| 171 | + "what does the type system already know?" This is a discipline, not a |
| 172 | + technique that can be automated. |
| 173 | + |
| 174 | +### Neutral |
| 175 | + |
| 176 | +- The underlying reversibility design decisions are documented fully in: |
| 177 | + - `007/docs/session-2026-04-12-jtv-v2-reversibility-design.adoc` |
| 178 | + - `julia-the-viper/docs/language/DESIGN-JTV-V2-REVERSIBILITY.md` |
| 179 | + - `nextgen-languages/docs/design/jtv-007-reversibility-fork.adoc` |
| 180 | +- TypeLL does not implement the 007 reversibility system; this ADR records |
| 181 | + the design patterns as transferable knowledge, not as an implementation |
| 182 | + commitment. |
0 commit comments