-
Notifications
You must be signed in to change notification settings - Fork 89
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
The "no LineTerminator
here" restriction before the brace in match
is inconsistent with existing control flow operators
#68
Comments
LineTerminator
here" within match
is inconsistent with existing control flow operatorsLineTerminator
here" restriction before the brace in match
is inconsistent with existing control flow operators
Essentially it boils down to, either we use an existing reserved word, or we have a NLT restriction. Existing words that have other meanings - including case - are likely to garner objections because of various forms of confusion, and the unused reserved words are unlikely to be applicable. I don’t think there’s any other option here besides NLT - while it’s unfortunate if JS forces a specific style, a) NLT has other precedent in the language, and b) the style that’s unintentionally forced is, at least, widely preferred in the community (which isn’t a justification but certainly mitigates the turbulence that might otherwise be caused). |
That is true, and 1TBS/Stroustrup style is so prevalent tools have been known to forget about Allman style on occasion when handling JS code. Also, we do kind of force it some with I filed this more as a thing that should be addressed explicitly, so we are at least aware of the potential confusion. (Syntax errors from erroneously using Allman style are going to be misleading unless engines/parsers specifically check for |
@isiahmeadows I'm honestly not sure how appropriate using if match (foo)
{
...
}
if match
(foo) {
...
}
if
match (foo)
{ ... } And it has the benefit that it doesn't need a cover grammar at all. I considered using |
Can you explain why that is? |
|
I mean
That bothers you too? (I can understand not wanting a |
That bothers me less, but bothers me still - i want no overlap with switch in any way, so i can continue to teach that switch statements are abomination (and hopefully teach that this proposal is the solution) |
@ljharb BTW, I meant |
I can't believe I didn't remember this, but... Both Erlang and Elixir, two of the most prominent languages that use pattern matching, use Combine this with the fact that using that keyword would solve pretty much all grammar-related issues... I think I'm on board with renaming this to |
And yes, as the name for what we now call |
Fixes: #68 This renaming removes all outstanding grammar-related concerns and greatly simplifies things. It also has precedent: Elixir and Erlang are the two most prominent dynamic languages with pattern matching support, and are both languages whose semantics this proposal draws from. It seems perfectly appropriate to use their name for this construct rather than stick to `match`.
Fixes: #68 This renaming removes all outstanding grammar-related concerns and greatly simplifies things. It also has precedent: Elixir and Erlang are the two most prominent dynamic languages with pattern matching support, and are both languages whose semantics this proposal draws from. It seems perfectly appropriate to use their name for this construct rather than stick to `match`.
I agree with the general sentiment that we should avoid cover grammars and excessive line terminator rules if possible. @ljharb If this construct effectively replaces and eliminates the need for the |
@zenparsing it's one thing to disambiguate a grammar, and another to disambiguate it when teaching people significantly new concepts. |
@zenparsing since switch will always exist, teaching the difference is most easy when the visual/syntactic difference is widest (while still intuitive for the new thing, ofc). |
@zenparsing Reusing switch (expr) { } Currently, this doesn't throw. But the current |
@isiahmeadows Good point. One valid approach to introducing a new pattern matching control flow construct in JS is to create something entirely new (e.g. a new expression form, with a new keyword, instead of a statement). Another valid approach would be to take the existing control flow construct and improve it (e.g. by introducing a new form of If one were going to use the second approach, I think allowing switch (expr) {} to continue to be a no-op would be reasonable. The broader point that a new form of |
@zenparsing I personally agree with @ljharb that we do need some level of distinction. I could see a variety of points of confusion among people learning the syntax (especially newer people to JS):
These are obvious to us designing the feature, but not necessarily to those who simply using it. At least using a different keyword (any keyword) to kick off the brain to visually parse it differently from a procedural C-style |
Sure, but it's not free: we have to pay with cover grammars and newline restrictions if we use a contextual keyword, or mental tax if we overload an existing keyword. In any case, I'm not particularly arguing for |
Piggybacking a bit on #77, if this proposal was to use const val = switch (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)
}; |
What about repurposing eg; const val = with (res) {
{status: 200, headers: {'Content-Length': s}} => `size is ${s}`,
{status: 404} => 'JSON not found',
{status} if (status >= 400) => throw new RequestError(res)
} Of course, this would be super confusing to explain, eg "JavaScript has a new pattern matching feature called 'with'! Oh and and by the way there was an old feature called with that you've probably never heard of and should never use". Personally I'd much prefer |
I'd be pretty against repurposing anything; I'm not interested in making it harder to learn JavaScript than it already is. |
@ljharb is it possible to add a new |
@borela no, more modes aren’t an option (desirable or achievable). |
One alternative is to require a prefix keyword that would allow parsers to disambiguate it with a look-ahead. For example, the function match () {}
match(...)
{
case ...
} This would mean that a look ahead for the first keyword const val = match (res) {
case {status: 200} =>
`size is ${s}`
case {status: 404} =>
'JSON not found'
} That said the For example consider the following, one might assume val would be undefined instead of the length of the array.
Using an explicit return might look like: const val = match (res) {
case {status: 200}:
return `size is ${s}`
case {status: 404}:
return 'JSON not found'
case {status}:
if (status >= 400)
return new RequestError(res)
} and avoids the afore-mentioned issues. |
match (foo) {
bar => ..., // `bar` unconditionally captures the argument
} There's been talks elsewhere before (I forget where) about adding an
No, I'm referring to this (note the top-level rest parameter): match (window.foo = 1, ...otherArgs)
{
// ...
} |
@isiahmeadows |
Also, my explanation was that we could upgrade |
I'm totally onboard with |
Personally I don't understand the hate on |
@borela The term This is why |
@isiahmeadows That makes sense. Thank you. |
Brain dump of possible varients: match (value) for {
case "1" => expression
case "2" case "3" => expression
}
// easiest on the tongue.
match (value) with {
case "1" => expression
case "2" case "3" => expression
}
// explicitly implies an expression is evaluated.
match (value) return {
case "1" => expression
case "2" case "3" => expression
} |
@thysultan Not a fan of the extra keywords here... (BTW, I did come up with a way to require very minimal edits to the current proposal, so I'm no longer convinced we must do anything drastic.) |
I feel uncomfortable with both the current heuristics that prevents a line break before That is to say – syntax that breaks away from a design that allows all of the following to be considered valid syntax. <if|else|switch|while> (expression) {
}
<if|else|switch|while> (expression)
{
} Even as an edge case, the fact that the following could break existing/future code also doesn't sit well. match (res)
{
foo => bar
} But at least that could be fixed by the use of a prefix keyword, for example – match (res)
{
case foo => bar
} I think the other mentioned issues would contribute to more "WTF" moments when learning, using and teaching JavaScript. |
Wouldn't be the first. ES6 did it with a less obscure case that could've been useful, in both strict and sloppy mode: var let = {}
var foo = "foo"
function store(value) {
// ES5: This assigns `value` to `let["foo"]` and returns `"foo"`
// ES6: This assigns `value[0]` to `foo` and returns it
let [foo] = value
return foo
} This one would qualify as less breaking, since the use cases are incredibly obscure. ( |
This is the longest issue thread in this entire repo. I would like to propose going back to @isiahmeadows's proposal of using Pros:
Cons:
I would appreciate it if y'all would consider this again, as I still think it's the most elegant solution to a bikeshed that continues to get longer. |
A case..match statement would have the same benefits as case but without the cons of |
Not necessarily a blocker, but I believe using
Since we're inside a |
For those as confused as I was. I would submit that if we don't want the backtracking that parsing until the switch (true) {
case true:
{
case (value) {
pattern => {}
}
}
} |
We should try to maintain a very high bar for introducing both cover grammars and one-off grammar restrictions. |
@loganfsmyth it's pretty unambiguous by looking one token past the (). Since you just need to spot the |
Slightly extending my suggestion in 376844097, here is something possibly silly but still possible: const val = switch * (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)
}; The
Afaik this doesn't generate any ambiguity, since until now Using the |
I continue to feel very very strongly that the word "switch" and "case" must be avoided, and I don't see the NLT requirement as problematic. Additionally, the NLT requirement is something that a number of existing proposals will need in order to be viable, so citing that as a reason to change this proposal will set an unfortunate precedent that I think is best avoided. |
@ljharb What do you think of adding these two restrictions to avoid the NLT requirement?
I don't believe this is too much to do, since:
Could you give examples of these? I'm not quite familiar with any others that have had NLT-specific issues. (I'm familiar with quite a few with ASI issues, but that's about it.) |
@isiahmeadows Regarding item 3, that’s true today, but it would stop being true if |
Okay. (And I should've known at least the block params one - I've actually discussed it some with the proposal's author 🙂) |
@ljharb So in light of those, I'm not super against closing this (but I'd like feedback on if it still needs to remain open). |
@ljharb I can't see how the protocols and extended-numeric-literals proposals will need NLT? That said seeing as this is intended as a new control flow operator in the same space as if, switch, while, etc, don't you think these all serve as precedence *against* NTL? |
@thysultan |
I'm gonna consider this specific problem resolved. Most of the reaction in-committee seemed to be that Because this issue has become so long and hard to parse, I'm going to close this. If anyone has specific proposals for alternative names that DO NOT USE NLTH, then please open an issue for that specific suggestion. NLTH was deemed pretty much a non-starter by a few committee members and I think it's safe to say we won't be able to ship this proposal if that stays, period. |
All existing control flow operators allow a line break before their block bodies, including with
switch
, and there's even an ESLint rule that can enforce it as a preference.This proposal blocks it for clear ASI concerns*, but for some teams, this could prove to be a problem (not all JS users use 1TBS or Stroustrup style**), and I don't believe we should be enforcing this opinion now after not having enforced it previously.
I have previously proposed (in multiple places, although with no specific bug) that we use
case
instead ofmatch
, although @ljharb has voiced some opposition in one of them.* In a potentially statement-terminating context,
match
could end up interpreted as a call rather than an expression when a newline is present.** It's not uncommon among C# or Windows devs who happen to write JS on occasion, since Allman style is the default VS style for C/C++/C# there. (I've also seen it a few times with Java.)
The text was updated successfully, but these errors were encountered: