CEP-0002: Pattern Matching and Narrowing¶
Draft
Draft proposal for future pattern matching, exhaustiveness checking, and runtime narrowing. V1 has conditionals, optional binding, enum values, enum unions, and closed error sets, but no match form.
Summary¶
Catalyst should grow a pattern-matching form for closed value families such as plain enums, enum unions, closed error sets, concrete type unions, and future discriminated unions.
Example¶
Possible future shape:
match value {
String => ...
Number => ...
Bool => ...
}
The first accepted version should treat narrowing as a semantic typing operation, not as ad hoc control-flow sugar. When a branch proves a case or member, the branch body should see the narrowed value type and any facts that follow from the successful test.
Motivation¶
V1 can express optional payload binding and ordinary conditionals, but several future features need a general narrowing story:
- exhaustive handling of enum unions and closed error sets;
- ergonomic payload access for discriminated unions;
- concrete type unions from CEP-0001;
- an
isoperator or equivalent type/pattern test; - diagnostics for unreachable, duplicate, and non-exhaustive cases.
Without a common model, each feature would invent its own narrowing rules and tooling would have to explain similar control-flow facts several different ways.
Proposed Direction¶
Pattern matching should be explicit source syntax. It should not discover cases through reflection at runtime or depend on hidden dynamic lookup.
The likely first targets are:
- plain enum cases;
- enum-union alternatives;
- closed error-set cases;
- concrete type-union members once CEP-0001 exists;
- future discriminated-union cases.
Branch coverage should be checked when the scrutinee type is closed. Open constraints, dynamic contract objects, and unconstrained runtime values should require explicit fallback handling unless a later proposal gives them closed pattern semantics.
V1 catch already reserves the unambiguous binding form expr catch as err { ... }. A future pattern-matching design may extend error handling with a catch switch form for Zig-like case handling without adopting pipe captures:
var bytes = read(path) catch switch {
ReadError.FileNotFound => empty_bytes
else as err => {
diag.err(err)
return LoadError.LoadFailed
}
}
The exact catch switch grammar is part of this CEP, not V1.
Narrowing¶
A successful branch narrows only inside the branch's control-flow region. Narrowing facts should be usable by type checking, diagnostics, and reflection predicates where those APIs already carry facts.
The same narrowing model should support a simple test form such as x is Pattern if that operator is accepted later. is is already reserved for future runtime type or pattern tests.
Open Questions¶
- What is the exact
matchsyntax? - Are guards part of the first version?
- How do destructuring patterns compose with V1 binding destructuring?
- Does matching consume, borrow, or copy payloads by default?
- How are range patterns represented and checked?
- How do diagnostics display normalized union and error-set cases?
V1 Compatibility¶
V1 remains unchanged except that catch binding uses catch as name, which leaves room for future catch switch pattern handling. Pattern-matching syntax is rejected, and active docs should refer to this CEP instead of carrying possible future branch syntax as current language reference.