Repository navigation
Syntax feature layering #306
Description
Note
I'd like to continue to update this if it is necessary. This issue currently reflects what we have (in #293).
I'm trying to refactor what ideas we have based on the layering idea. This is also an answer to #305, #281
By layering, I do not mean we must have all features in layer 1 or must defer all features in layer 2.
This layering mostly focused on syntax, not runtime semantics.
I'll analyze this based on whether we can desugar a pattern into the composition of other patterns. (So you'll find the Primitive pattern (1 in { x: 1 }) is in Layer 2.)
This means we can pick all features from Layer 0 and some features from Layer 1 and 2, then the results become a somewhat usable pattern-matching proposal. (You may want to take a look at https://github.com/codehag/pattern-matching-epic).
I include:
- all "future extensions" (= do not expect it will be in the final proposal, marked by 🤔)
- features without consensus (might be incorrect!) (also marked by 🤔)
Non-pattern structures
Level 0: Basic feature
We should have at least one of them to enter the pattern world.
matchexpression (match (expr) { ... })- basic clause (
when, or no keyword at all) (when <pattern>: <expr>,or<pattern>: <expr>) - basic clause with guard (
when <pattern> if (<expr>): <expr>,or<pattern> if (<expr>): <expr>)- 🍬 desugar: can be removed if we have the
ifpattern
- 🍬 desugar: can be removed if we have the
defaultclause (default: <expr>)ifclause (if (<expr>): <expr>)- 🍬 desugar: can be removed if we have the
ifpattern
- 🍬 desugar: can be removed if we have the
- basic clause (
isexpression- 🍬 desugar: impossible if the
isexpression enables the possibility to declare a new variable inside an expression ⚠️ concern: block scoped declaration{ if (!(x is [let a, let b])) return; f(a, b) }
- 🍬 desugar: impossible if the
Level 1: Good to have
Maybe future extensions.
if-let(and alsofor,while,do-while)- based on the
isexpression - or something else like
if match <pattern> (expr) {}
- based on the
- 🤔 try-catch integration
- 🤔 async match
- 🤔 match on function parameter
- 🤔 function overload based on parameter matching
Pattern
Level 0: Basic features, impossible to desugar
- Variable pattern (
expected_value)🍬 desugar with custom matcher: write a function to match the value
- Object pattern (
{ a: expected_value })🍬 desugar with custom matcher: write a function to match the shape and return value as an array⚠️ concern:{ __proto__: f }means property__proto__(owned or on the prototype) or[[Prototype]]or early errors?⚠️ concern: Computed property ({ [Symbol.iterator]: f }) adds arbitrary expression interpolation⚠️ concern: Shorthand property ({ x }) means{ x: x }or{ x: let x }?⚠️ concern: Rest property without matcher ({ ...x }) means{ ...x: x }or{ ...x: let x }?
- Binding pattern (
{ a: let x })
Level 1: Advance features, impossible to desugar
- 🤔 Expression pattern (
${expression})⚠️ concern: arbitrary expression interpolation⚠️ concern: interact with custom matcher
- Untagged template literal with interpolation (
{ id: let id, b: `id-${id}` })⚠️ concern: arbitrary expression interpolation⚠️ concern: blocks feature that TS have:`a-${let x}-z`matchesa-99-zand bindxto"99".
- Iterator pattern (
[1, 2])- 🍬 desugar: Impossible for iterators
- 🍬 desugar for Array:
{ 0: let a, 1: let b }. - 🍬 desugar for Array with custom matcher:
Array(let a, let b). ⚠️ concern: ...rest consuming
- Custom matcher pattern (
{ x: Point }) and extractor pattern ({ x: Point(let x, let y, let z) })⚠️ concern: performance of creating match result objects Vague runtime performance concerns about custom matchers #253 and Splitting Variable/Extractor matcher hook? #303⚠️ concern: no way to pass extra parameters / generate a custom matcher on the fly- unless we have expression pattern
{ x: ${SomeFun(param)}(let a, let b) }
- unless we have expression pattern
⚠️ concern: syntax share with variable pattern- cannot know if
{ x: Point }will call user code or do=== Pointcheck until runtime - cannot force to do the
=== Pointcheck- unless we have
ifpattern andandpattern{ x: let x and if (x === Point) } - unless we have
===relational pattern{ x: === Point }
- unless we have
- cannot know if
- 🤔 Auto-generated matcher function for class syntax
⚠️ concern: private field performance problem⚠️ concern: hackable problem⚠️ concern: interact with Symbol.hasInstance
andpattern (Map and { size: 1 })- 🍬 desugar: Possible only for a few use cases, e.g.
{ a: Point and let a }=>{ a: Point, a: let a } ⚠️ concern: syntax diverge with bool&&
- 🍬 desugar: Possible only for a few use cases, e.g.
orpattern (1 or 2)⚠️ concern: interact with binding pattern⚠️ concern: syntax diverge with bool||
notpattern (not 1)⚠️ concern: interact with binding pattern⚠️ concern: syntax diverge with bool!
- 🤔
ifpattern (if (expr))⚠️ concern: arbitrary expression interpolation⚠️ concern: interact withisexpression
- 🤔 Default values (
{ a = 1 })⚠️ concern: arbitrary expression interpolation
Level 2: Ergonomic features
- Simple literal pattern (
null,true,1,2n,"str",NaN,`abc`)- 🍬 desugar: extract
let x = ...;somewhere then match againstx
- 🍬 desugar: extract
- Near literal pattern (
undefined+1-1+1n-1n, 🤔+Infinity, 🤔-Infinity)⚠️ concern: about+Infinityand-Infinity- or more powerful version: Unary algebraic pattern
- 🤔 Unary algebraic pattern (
+0,-x)⚠️ concern: what RHS should be? number literal and (dotted) identifier?⚠️ concern: open the door to defining "simple expression" (a rabbit hole) to do matha + 1
- RegEx literal pattern (
/a/)- 🍬 desugar (with custom matcher): extract
let x = ...;somewhere then match againstx({ groups: { ... } })
- 🍬 desugar (with custom matcher): extract
- 🤔 RegEx named capture group bindings (
/a(?<let name>)/)- 🍬 desugar:
/a(?<name>)/({ groups: { let name } }) ⚠️ concern: since we cannot add all patterns we have into RegEx syntax, only allow binding looks not so useful
- 🍬 desugar:
- Extended variable pattern (
{ value: Number.POSITIVE_INFINITY }, 🤔{ x: Array[Symbol.iterator] })- 🍬 desugar: extract
let x = ...;somewhere then match againstx ⚠️ concern: cache[[Get]]of"POSITIVE_INFINITY"onNumber?⚠️ concern: arbitrary expression interpolation (forident[expr]syntax)
- 🍬 desugar: extract
- Shorthand property binding (
{ let x })- 🍬 desugar:
{ x: let x }
- 🍬 desugar:
- 🤔 Shorthand object pattern (
let { x, y })- 🍬 desugar:
{ x: let x, y: let y } ⚠️ concern: visual confusion
- 🍬 desugar:
- 🤔 Relation pattern (
>= 2)- 🍬 desugar with
ifpattern andandpattern:let x and if (x > 20 && x < 30) ⚠️ concern: what RHS should be? number literal and (dotted) identifier?> 2,< 2- 🤔🤔
instanceof x⚠️ concern: some of us don't like instanceof
- 🤔🤔
=== x⚠️ concern: looks strange{ constructor: === Point }
- 🤔🤔
"x" in⚠️ concern: looks strange{ x: "a" in }
- 🤔🤔
typeof "x"⚠️ concern: we already choose custom matchers to match like{ x: Number }, not{ x: typeof "number" }
- 🍬 desugar with
- 🤔 Void pattern (
{ a: 1, b: void })- 🍬 desugar: create a new unused binding
{ a: 1, b: let _ } - Note: because we need to also add it into destructing and
using.
- 🍬 desugar: create a new unused binding
- 🤔 Optional property pattern (
{ a: 1, b?: 2 })- 🍬 desugar (with
or):{ a: 1 } or { a: 1, b: 2 } - 🍬 desugar (with default value):
{ a: 1, b = undefined }
- 🍬 desugar (with
- 🤔 Binding-while-matching property pattern (
{ let a: Number })- 🍬 desugar (with
and):{ a: Number and let a }
- 🍬 desugar (with
- 🤔 Chaining guards
Activity