Mark Cohen, Tab Atkins-Bittner, Jordan Harband, Yulia Startsev, Daniel Rosenwasser, Jack Works, Ross Kirsling
This is an update, not a request for stage advancement
Priorities
Priority: pattern matching
Many ways to match values in JS
No way to match patterns
Construct contains more than just patterns
We will prioritize the ergonomics of patterns where conflicts arise
Priority: subsumption of switch
Easily googleable; zero syntactic overlap
Reduce reasons to reach for switch
Preserve the good parts of switch
Priority: be better than switch
No more footguns
New capabilities
Priority: expression semantics
Pattern matching construct should be usable as an expression
return match { ... }
let foo = match { ... }
etc
Priority: exhaustiveness and ordering
Fall-through and "no match" should be opt-in, not opt-out
Execution order should never be surprising
Priority: user-extensibility
Userland objects and classes should be able to encapsulate their own matching semantics
Questions before we see some syntax?
match (res) {
when ({ status: 200, body, ...rest }) {
handleData(body, rest);
}
when ({ status: 301 | 304, destination: url }) {
handleRedirect(url);
}
when ({ status: 404 }) { retry(req) }
else { throwSomething() }
}
match (res) {
// match (matchable) {
when ({ status: 200, body, ...rest }) {
// when (pattern) { ... }// ───────↓────── ───↓───// LHS RHS (sugar for do-expression)// ───────────↓──────────// clause
when ({ status: 301 | 304, destination: url }) {
// ↳ pipe is logical OR// ↳ `url` is an irrefutable match, functions as renaming
else { ... }
// ↳ cannot coexist with top-level irrefutable// match, e.g. `when (foo)`
match (command) {
when (["go", ("N" | "E" | "W" | "S") as dir]) { ... }
when (["take", item]) { ... }
else { ... }
}
match (res) {
if (isEmpty(res)) { ... }
when ({ numPages, data }) if (numPages > 1) { ... }
when ({ numPages, data }) if (numPages === 1) { ... }
else { ... }
}
match (res) {
if (isEmpty(res)) { ... }
when ({ data: [ page ] }) { ... }
when ({ data: [ frontPage, ...pages ] }) { ... }
else { ... }
}
match (arithmeticStr) {
when (/(?<left>\d+) \+ (?<right>\d+)/) { ... }
}
constLF = 0x0a;
constCR = 0x0d;
match (token) {
when ^LF | ^CR { ... }
}
className {
[Symbol.matcher](matchable) {
const pieces = matchable.split(" ");
if (pieces.length === 2) {
return pieces;
}
}
}
match ("Tab Atkins-Bittner") {
when ^Namewith [ first, last ] if (last.includes('-')) { ... }
when ^Namewith [ first, last ] { ... }
}
Lightning round: add-ons
async match
async match (awaitauth()) {
when ({ user: ^(awaitgetUser()) }) { ... }
else { awaitgetError() }
}
& combinator
match (getFromDB()) {
when (^FancyError) { ... }
when (^AggregateError & { errors: [ ^TypeError, ...rest ] }) { ... }
when (^AggregateError) { ... }
}
Nil pattern
match (someArr) {
when [_, _, someVal] { ... }
}
catch guards
try {
doSomething();
} catch match (err) {
if (err instanceofRangeError) { ... }
when (/^abc$/) { ... }
// default: else { throw err; }
}
Pattern matching updates 20 April 2021 This proposal was formerly being authored and championed by Kat Marchán, before they left TC39. A new group of champions has taken the proposal back up with a new direction.