Proposal-pattern-matching: Add match case indicator to proposal?

Created on 16 Apr 2018  Â·  10Comments  Â·  Source: tc39/proposal-pattern-matching

I had a bit of a discussion a while back with @zkat on Twitter about this possibility. I wonder if adding something like the pipe character to the beginning of each case in a match would make it easier to parse. Something like this:

const res = await fetch(jsonService)
const val = match (res) {
  | {status: 200, headers: {'Content-Length': s}} => `size is: ${s}`
  | {status: 404} => 'JSON not found'
  | {status} if (status >= 400) => throw new RequestError(res)
}

This is very similar to ReasonML's pattern matching and could make parsing match statements a bit easier especially with multi-line statements:

// this
const res = await fetch(jsonService)
const val = match (res) {
  | {
    status: 200, 
    headers: {
        'Content-Length': s
    }
  } => `size is ${s}`
  | {status: 404} => 'JSON not found'
  | {status} if (status >= 400) => throw new RequestError(res)
}

// vs. this
const res = await fetch(jsonService)
const val = match (res) {
  {
    status: 200, 
    headers: {
        'Content-Length': s
    }
  } => `size is ${s}`
  {status: 404} => 'JSON not found'
  {status} if (status >= 400) => throw new RequestError(res)
}

Most helpful comment

Yeah I don't find the |] very readable. I'm also pretty opposed to randomly chucking new glyphs (or glyph combos) in like that. I'd rather keep things feeling _closer_ to JS.

And yes, I do feel like it's important to distance match from switch -- though I'm still open to using either case (..) {...} or super switch (...) {...} as alternatives. I don't really like using case <pattern>:, because of the similarity to switch, but I guess I can be convinced if that's the only way to resolve parsing issues.

I am, though, tremendously uninterested in bikeshedding anything over simple "taste" or arbitrary preference. I've been largely focusing on general distancing + parsing concerns in my decisions surrounding mechanics+syntax -- I'm looking more for existing, corresponding constructs that would make this easy to understand. And I think distance from switch, because of the significant difference in semantics, is pretty important.

All 10 comments

Is there any way that this could affect the dynamics of bitwise operations and minification, For example:

match (res) {
    | {status: 404} => 'JSON not found' | res => res
}

That is a good question, and definitely a consideration. That would be an implementation question, but definitely one to consider. It could still end with a , which would help with that kind of thing.

One thought might be to use something like this, which could allow you to match multiple patterns on a line:

match (res.status) {
    |] 404 |] 'not_found' => 'JSON not found' | res => res
}

This is probably an unpopular opinion, but I think adding more symbols that means something different than what it normally means adds that extra cognitive step, especially to beginners (i.e. | already means bitwise or and now it means something else, => means lambda and now it's something else). When match expressions contains actual bitwise or and lambdas, it'll look even more confusing.

I think this may be clearer:

const val = match (res) {
  case {status: 200, headers: { 'Content-Length': s }}:
    `size is ${s}`;
  case {status: 404}:
    'JSON not found'
  case {status} if (status >= 400):
    throw new RequestError(res);
  default:  // same as case x but ignoring the capture variable
    throw new Error("unexpected");
}

A beginner would see this and may immediately think, "oh, it's like a switch, but no fall-throughs and can have variables in the cases". As opposed to the mental overhead of parsing "is that | or => part of the case or is it part of the expression that goes with the case?".

“It’s like a switch” is something i feel very strongly that users shouldn’t be thinking. Switch needs to die; this is a new, better thing.

I believe that @zkat feels the same way as @ljharb, based on our Twitter convo. I think maintaining a resemblance to other languages, while make this proposal inherently Javascript-y is going to be difficult, but would help a lot.

Many symbols in JS are already taken, but I think a combined two characters (like |]) would be clear and is similar to the pipe operator (|>) that already has a proposal. It also isn't a character combination that should ever happen in current JS, which means that minifies should be alright parsing that.

Yeah I don't find the |] very readable. I'm also pretty opposed to randomly chucking new glyphs (or glyph combos) in like that. I'd rather keep things feeling _closer_ to JS.

And yes, I do feel like it's important to distance match from switch -- though I'm still open to using either case (..) {...} or super switch (...) {...} as alternatives. I don't really like using case <pattern>:, because of the similarity to switch, but I guess I can be convinced if that's the only way to resolve parsing issues.

I am, though, tremendously uninterested in bikeshedding anything over simple "taste" or arbitrary preference. I've been largely focusing on general distancing + parsing concerns in my decisions surrounding mechanics+syntax -- I'm looking more for existing, corresponding constructs that would make this easy to understand. And I think distance from switch, because of the significant difference in semantics, is pretty important.

If you want pre-existing constructs, the case statement you describe is actually already a pre-existing thing in Ruby.

case foo
when "A", "B"
  ...
when "C"
  ...
else
  ...
end

case
when foo.match(/\d/)
  ...
when foo.match(/[a-zA-Z]/)
  ...
end

It's basically a switch statement except without the fallthrough we all hate. Which is half of what we want. Instead the comma allows multiple values for the same case.

What if we avoided the other arguments about the => confusion by using match instead.

const val = case ( res ) {
match {status: 200, headers: {'Content-Length': s}}: `size is ${s}`;
match {status: 404}: 'JSON not found';
match {status} if (status >= 400): throw new RequestError(res);
}
  • A case statement already has a pre-existing analog in another language for "a switch statement with no fallthrough"
  • The match makes it clear we aren't doing anything with equality but instead are writing a match expression.
  • IIRC case is a reserved word so there should be no ambiguity between the statement and expression forms like with match(...) {}.
  • This also leaves open room for us to instead decide to make case a first-class replacement for switch in the non-match cases. i.e. Allow for migration of switch ( foo ) { case 'bar': doBar(); break; case 'baz': doBaz(); break; } to something like case ( foo ) { when 'bar': doBar(); when 'baz': doBaz(); } and even allow for mixing of simple equality cases with match cases.

The latest version of this does this. when is now the case indicator. So I'm gonna go ahead and close this as resolved :)

Love the when! Solves all the problems I originally had. :D

Was this page helpful?
0 / 5 - 0 ratings

Related issues

xtuc picture xtuc  Â·  4Comments

elijahdorman picture elijahdorman  Â·  6Comments

tabatkins picture tabatkins  Â·  4Comments

JLHwung picture JLHwung  Â·  3Comments

MoritzKn picture MoritzKn  Â·  5Comments