ADR-160: A Set With Two Error Types In It Is a Generated Sum

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

1. Context

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.

2. Decisions

D1 — A set of two or more named members is one generated 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.

D2 — Each member carries the channel it would have had alone.

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.

D3 — A 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.

D4 — A 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.

3. Consequences

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.

4. What this does not decide

5. What is built

This site is open source. Improve this page.