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)
}
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);
}
case statement already has a pre-existing analog in another language for "a switch statement with no fallthrough"match makes it clear we aren't doing anything with equality but instead are writing a match expression.case is a reserved word so there should be no ambiguity between the statement and expression forms like with match(...) {}.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
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
matchfromswitch-- though I'm still open to using eithercase (..) {...}orsuper switch (...) {...}as alternatives. I don't really like usingcase <pattern>:, because of the similarity toswitch, 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.