Proposal-pattern-matching: Possible to use pattern-matching with existing control structures?

Created on 21 Apr 2021  路  11Comments  路  Source: tc39/proposal-pattern-matching

First of all, looks like an interesting proposal!

I think it would be useful to be able to use pattern matching with existing control structures too.

  • allow pattern matching + destructuring to be used in more places
  • easier to learn, less new syntax
  • possibly reduce the complexity/scope of this proposal

I use the infix keyword "matches" in the following examples, however other options could be considered.

For example:

if (res instanceof Response && res matches { status: 200, content }) {
    return content;
}
// do something else

const arr = [];
while (iterable.next() matches { done: false, value }) {
    arr.push(value);
}
champion group discussion design idea

Most helpful comment

@jamiebuilds that's captured by #191 :-)

All 11 comments

You certainly can do this, since the match construct is an expression:

if (match (res) { when ({ status: 200, content }) if (res instanceof Response) { true } else { false } }) {
    return content;
}

I don't think it'd be worth adding _another_ syntax form just so it reads more sentence-like :-)

Does that meet your needs?

There is actually prior art for this: https://doc.rust-lang.org/std/macro.matches.html

if res.is_response() && matches!(res, Response { status: 200 }) {
}

I think this may be worth considering.

Here's your second example, using the stage 2 iterator helpers proposal, if it ends up including takeWhile:

const arr = iterable.takeWhile(x => match (x) { when(^value) { true } else { false } }).toArray();

You certainly can do this, since the match construct is an expression:

if (match (res) { when ({ status: 200, content }) if (res instanceof Response) { true } else { false } }) {
  return content;
}

I don't think it'd be worth adding _another_ syntax form just so it reads more sentence-like :-)

Does that meet your needs?

Thanks for the quick reply!

Not to discredit your impressive work, but this doesn't seem very readable to me 馃槃 ?

I didn't see this described in the README, although perhaps I missed this? If not, might be a good idea to document with a few examples?

@mpenney99 Since the matches!() is a macro in Rust, maybe it could be later replicated via babel-macro?

I agree it's not very readable - I'd personally not put matching logic inline anywhere like that, i'd store the result in an intermediate variable. I'm not sure examples with poor readability are useful in the readme :-)

Here's one way i'd personally write it:

const matched = match (res) {
  if (res instanceof Response) when ({ status: 200, content }) {
    true;
  }
  else {
    false;
  }
});
if (matched) {
    return content;
}

Okay, I had another look at this. In your first example, you wouldn't be able to expose "content" to the scope of the if-block, right? It would only be available within the scope of the match() expression?

Anyway, I think the fundamental problem here is that match() is designed as a replacement for the switch block. I definitely agree it's worth-while to address some of the existing limitations and foot-guns here (accidental fall-through, assigning the result to a variable). And there's a lot of prior-art here too (even Java has switch expressions now!).

However, for me it still feels like pattern-matching is kind of a separate concern (but could be you disagree!). I think it would be good to consider how pattern-matching could be used alongside existing structures and also with imperative code.

@mpenney99 aahhhh yes, that's true. in that case:

match (res) {
  if (res instanceof Response) when ({ status: 200, content }) { return content; }
  else {}
};

should do it?

I notice the pattern match (x) { when(^value) { true } else { false }. It looks like if (sth) return true; else return false; to me

Rather than extending other statement grammars, it would be rather nice to have an instanceof-like operator that allowed you to compare any expression against a pattern.

Just to keep it separate from the current proposal syntax to avoid any confusion, I'm going to us a is operator:

<expression> is (<pattern>)
if (res is ({ status: 200 })) {
  // ...
}

I don't believe there's any existing mechanism like this, but ideally this syntax would also allow you to create bindings in the "current" scope (block-scoped, similar to how for (let ...) works today):

if (res is ({ status: 200, body })) {
  console.log(body)
}

There is also prior art for this sort of behavior in Rust:

if let Some(value) = res {
  println!("{}", value)
}
// same as:
match res {
  Some(value) => {
    println!("{}", value)
  }
}

The particular syntax of this pattern matching proposal does get a bit weird looking with an if clause:

if (res is ({ status }) if (status >= 400)) {
  // ...
}

I'd argue that's acceptable weirdness though.

@jamiebuilds that's captured by #191 :-)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

arcanis picture arcanis  路  6Comments

samuelgruetter picture samuelgruetter  路  3Comments

tabatkins picture tabatkins  路  4Comments

j-f1 picture j-f1  路  3Comments

Haroenv picture Haroenv  路  6Comments