Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Design Meeting Notes, 8/12/2022 #50325

Closed
DanielRosenwasser opened this issue Aug 16, 2022 · 1 comment
Closed

Design Meeting Notes, 8/12/2022 #50325

DanielRosenwasser opened this issue Aug 16, 2022 · 1 comment
Labels
Design Notes Notes from our design meetings

Comments

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Aug 16, 2022

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 do expression which doesn't exist yet.

  • What patterns are there?

    • Combined patterns
      • Pattern and Pattern
      • Pattern or Pattern
    • Interpolation patterns
      • ${ Expression }
    • Identifier patterns:
      • Infinity
      • NaN
      • undefined
      • new bindings otherwise (?)
    • Near-literal patterns: e.g. 0, "some string", null, RegularExpressionLiteral, and TemplateLiteral.
    • Array patterns
    • Object patterns
    • Parenthesized 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.matcher to return the things that you want to allow a user to bind/destructure after the pattern match.
      • There's a with keyword 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 with destructuring.
  • 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 match expression.

    • 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 {}.
    • 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 match expressions 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

  • Epics + Extractor Objects

    • is expression

    • 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 ... in sort of syntax.
      • Not certain about matching on type.
  • 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 named Foo
      • "What?"
      • Seems like this prioritization is backwards - matching an existing binding is the common case.
@DanielRosenwasser DanielRosenwasser added the Design Notes Notes from our design meetings label Aug 16, 2022
@Thaina
Copy link

Thaina commented 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 if and switch. That would allow solving #15480 and greatly satisfied similar case of type constraint for every type in almost any use cases

For example

function Grade(grading: number when (>= 0) & (<= 100) & (% 10 == 0))
{
   // grading are constraint to be 0 10 20 30 40 ... 90 100
}

Grade(10); // fine
Grade(19); // error
Grade(120); // error

let value = GetNumberValue(); // number
if(value >= 0 && value <= 100 && value % 10 == 0)
    Grade(value); // should not error if possible

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Notes Notes from our design meetings
Projects
None yet
Development

No branches or pull requests

3 participants