Status: Superseded by ADR-280 — its D1-D4 are D15-D18 there
Date: September 20, 2026
Target Version: Nikaia 0.0.77
Related: ADR-023 D1 (the set), D3 (an error type is an enum),
D4 (the two axes, and the named residue), D6 (an error knows its site),
ADR-157 (the channel, and the envelope), ADR-158
(std’s names), ADR-159 (a library’s type, bare),
ADR-146 D1 (NK1151), Part I 7.1, Part III C.1
ADR-023 D1 records throws as a set, and three records made a
set of one into a channel: the program’s own type
(ADR-157), then std’s names (ADR-158), then a
library’s type (ADR-159). What was left is the set of two, and
a program reaches it by doing two ordinary things at once: reading a file, and
throwing an error of its own.
That is not a corner. examples/sqlite/main.nika is already there —
throws = ["Refused", "io::IoError"] — and Part I 7.1’s own teaching program is
one line away from it.
Until now such a function fell back to the opaque channel, which was the honest
answer while no sum existed: a channel named after one of two error types
would be a lie. What the fallback cost is D4’s first row, again: the variants
within one error type are closed and a match over them is checked, and a
handler could not reach them.
enum, per set.Not per function. Two functions that fail the same way get the same type, so a
? between them converts nothing — which is what keeps propagation the plain
? the language below already writes.
The program never writes the name. A catch matches on the members’
variants (ADR-023 D4), so the type exists only in the generated
file, is spelled so that nothing a program declares collides with it, and is
defined once at the crate root and named crate::… everywhere — a module is
a file of its own below, and a sum written into each of them would be a
different type per file, with no way for a failure to cross a module boundary.
A set with a "?" in it is not a sum. A sum with a hole in it is the opaque
channel by another spelling.
A type this unit declares keeps its envelope — Thrown[E], with the site
ADR-023 D6 asks for, because the program throws it. A library’s
type is bare, for ADR-159 D2’s reason: no throw in this program
raised it, so there is no site to carry.
The sum adds nothing of its own. It is not where anything was raised, so its
full() asks the member.
match over a sum is taken apart by member.One match per member, carrying the arms whose patterns name that member’s
variants, and the source’s catch-all written into each of them — because the
members are separate types and the language below has no fall-through. Every
member gets an arm, including one the handler said nothing about, because the
sum is an enum below and a match over one must cover it; what runs there is
the catch-all, which is what the source meant by not naming it.
throw error inside a member’s arm puts the failure back in the variant it came
out of, with the site it was raised at where there is one.
match over a catch’s error with more than one type arriving needs else — NK1151.The variants within one error type are closed; the set of error types is
open (ADR-023 D4). So a handler that names variants of two of them
has covered no set at all: a callee that gains a failure sends a third type
there, and the match has nowhere to put it. else is what says the rest,
which is the answer ADR-146 D1’s code already gives for every type
whose cases cannot be enumerated in arms.
It is refused twice, which is the arrangement NK1132 already has: the
checker for the caret, and the lowering because it cannot write the file either
way.
examples/sqlite goes through it, so the sum has a user in the tree rather
than only in its tests.
The four error records now cover every shape a set can have. One named
member: the channel is that type. Two or more: the sum. A "?" anywhere: the
opaque channel, which is the honest answer for something this compiler cannot
name.
One rustc warning had to be allowed rather than avoided.
non_camel_case_types, on a name deliberately spelled so that nothing collides
with it. A warning about the generated file is a defect here
(Part III C.1), and sqlite3_from_end_to_end is the
test that said so.
mains
carry throws = ["?", "io::IoError"], and the "?" is always the same thing:
a grammar entry, whose failure is a rendered string and has no type. It is
ADR-158’s shape one library over, and until it is named those
programs keep the opaque channel. #179 carries it.NK2401 and the --locked diff (ADR-101), which is now the
only part of ADR-023 D1’s set that nothing reads.crate::… so that it
works; no multi-file program in the tree has a set of two, so nothing was
measured.Emitter::error_sums (per set, held rather than derived per
question), write_error_sums (once, at the root), sum_path.sum_member_type, and the From beside each sum so that a ?
converts on its own; Display, Error and Full delegate to the member.Emitter::match_over_a_sum and member_of; throw puts the member
back in its variant.Checker::a_match_over_several_error_types, raising NK1151; and
the lowering’s own refusal.crates/nikaia/tests/error_sums.rs, four of which compile and
run the program.