Repository navigation
Design Meeting Notes, 8/12/2022 #50325
Description
Pattern Matching
-
[[Overview of current pattern matching proposal.]]
-
Looks roughlylike
return match (subject) { when (0): expr1; when (1) if (test1): expr2 if (test2): expr3 default: expr4; } -
All RHSs are expressions, not statements. If you need statements, you need a
doexpression which doesn't exist yet. -
What patterns are there?
- Combined patterns
- Pattern
andPattern - Pattern
orPattern
- Pattern
- Interpolation patterns
${Expression}
- Identifier patterns:
InfinityNaNundefined- new bindings otherwise (?)
- Near-literal patterns: e.g.
0,"some string",null,RegularExpressionLiteral, andTemplateLiteral. - Array patterns
- Object patterns
- Parenthesized patterns
- Combined patterns
-
match ([1]) { when ([1, undefined]) ...- does this match?- No, incorrect number of elements.
- Differing behavior with
const [x, y] = [1]
-
One can use a value with a custom matcher within an
${interpolation pattern}.- Use method named by a proposed
Symbol.matcherto return the things that you want to allow a user to bind/destructure after the pattern match.- There's a
withkeyword that allows you to bind against that value. - What happens if you destructure properties from the returned object, but they don't exist?
- We believe the match still succeeds.
- Differing behavior between pattern matching and
withdestructuring.
- Differing behavior between pattern matching and
- We believe the match still succeeds.
- There's a
- Use method named by a proposed
-
Control-flow analysis implications for TypeScript
-
A match should narrow based on the implications of the thing being matched against.
declare const value: { x: 1 | 2 | "foo" } | { y: false } match (value) { // 'value' should be narrowed to '{ x: 1 | 2 | "foo" }' // Accessing 'value.x' should give a narrowed '1 | 2' when ({ x: Number }): value; }
-
-
One counter-proposal around "epics"
-
Break down functionality into smaller pieces, make things more teachable.
-
Matching becomes a single primitive that can be brought into several constructs, including maybe a new
matchexpression. -
let when, etc.let { body } when isOk(response);- Some concerns about this
- No initializer
- Single unary function is very specialized.
- Doesn't work for arrays due to iterable (but this is solvable).
- "Failure mode" for a match is silent - just falls back to
{}.
- Some concerns about this
-
Integration with control flow expressions
if (let { body } when isOk(response)) { // ... }for (let { body } when isOk of responses) { // ... }while (let { body } when isOk(responses.pop())) { // ... }- Some of these examples echo something very intriguing - but it seems like there's room to integrate on the how of making these consistent and compositional.
-
Then
matchexpressions would invert the difference between matching and binding.- Bindings now would need an explicit keyword instead of becoming the default behavior.
- Matching now becomes the default behavior.
- We highly prefer explicit binding - though we don't necessarily feel it should be separated from the pattern.
-
-
Extractor Objects Addition
- Inspired by Scala's
apply/unapplyif you're familiar, but not crucial. - Would leave room for first-class algebraic-data types/tagged objects.
- Inspired by Scala's
-
-
isexpression- RelationalExpression
isMatchPattern - Produces a boolean value.
- Similar to C#'s
isintegration with patterns if you're familiar with it, but not important.
- RelationalExpression
-
Allows you to match and bind.
-
Allows you to create a local from expression position.
if (container is Box and { containedValue: let value }) { // 'value' is now scoped and was set to 'container.value'. } -
Like the proposal direction.
- Cute that you can introduce new bindings - would prefer a
let ... insort of syntax. - Not certain about matching on type.
- Cute that you can introduce new bindings - would prefer a
-
-
Wait, what does
${and}from the original proposal mean again?- It's the difference between matcher vs binding.
${Foo}would say "the thing inside of the curlies (Foo) is a matcher" because otherwise you'd create a variable namedFoo- "What?"
- Seems like this prioritization is backwards - matching an existing binding is the common case.
- It's the difference between matcher vs binding.
Activity
Thaina commentedon Dec 7, 2022
I have an idea from the #15480 relate to pattern matching that, in typescript we could extend this feature more than C# to allow pattern matching directly as type constraint in addition to
ifandswitch. That would allow solving #15480 and greatly satisfied similar case of type constraint for every type in almost any use casesFor example