ADR 0018: Typed `catch` clauses match with `errors.AsType`
Status: Accepted (extends 0008)
Context
Section titled “Context”Backend code handles specific errors all the time: a missing row becomes a
404, a unique-constraint violation a 409, a timeout a retry. Go code writes
var nf *NotFound; if errors.As(err, &nf) { ... }, or since Go 1.26
if nf, ok := errors.AsType[*NotFound](https://github.com/lyth-lang/lyth/tree/main/docs/adr/err); ok { ... }, and must remember to
return every other error. With only catch (err), Lyth code wrote the same
dispatch by hand inside the handler, including the easy-to-forget
else { throw err }.
Decision
Section titled “Decision”e catch (x: T) { ... }handles errors of typeT, a non-null type that implementsError, and bindsxas aT. Several clauses may follow one another and are tried in order.- Matching uses
errors.AsType[T], so it sees through wrapping. That differs fromis(ADR 0016) on purpose:isasks about a value’s dynamic type, whilecatchasks about an error, and Go inspects errors through their chain. - Errors that no typed clause handles are still errors. The expression stays
fallible and must end with
?(propagate the rest) or with an untypedcatch(handle the rest). Otherwise it is E0303. - The chain lowers to one
if err != nilholding anif/else ifdispatch onerrors.AsType, ending in the final handler or inreturn ..., err.
Alternatives
Section titled “Alternatives”- Unmatched errors propagate implicitly, as in Java or Kotlin: shorter, but
it hides an exit point. In Lyth every propagation is visible as
?(ADR 0008). whenover errors withis/errors.Isconditions: more general, but it moves the handling away from the call and needs the error bound first. It remains possible later for sentinel values.- Type assertions (
err.(T)): miss wrapped errors, which are the common case once code useswrap. - Sentinel patterns (
catch (ErrNotFound)): sentinels compare witherrors.Is, and a value in the binder position would be ambiguous. An untypedcatchwitherrors.Iscovers them.
Consequences
Section titled “Consequences”The generated code is the dispatch a Go programmer writes, with no helper and
no allocation beyond errors.AsType’s own. It needs Go 1.26, which Lyth already
requires. A typed clause whose binder is unused names it _
(catch (_: *NotFound) { fallback }?), following the rule for every unused
name.