mdn / content

The content behind MDN Web Docs
https://developer.mozilla.org
Other
8.99k stars 22.44k forks source link

Issue with "Operator precedence": arrow function operator precedence #12914

Closed dy closed 2 years ago

dy commented 2 years ago

MDN URL: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence

What information was incorrect, unhelpful, or incomplete?

Maybe worth adding => operator precedence? It seems to be 2 - assignment group. TC39 members often use that precedence as argument.

MDN Content page report details * Folder: `en-us/web/javascript/reference/operators/operator_precedence` * MDN URL: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence * GitHub URL: https://github.com/mdn/content/blob/main/files/en-us/web/javascript/reference/operators/operator_precedence/index.md * Last commit: https://github.com/mdn/content/commit/680c881dc13b26f561bd9a35678e66c7b95f1e87 * Document last modified: 2021-12-28T17:25:57.000Z
hamishwillee commented 2 years ago

Not sure. There is this note in the docs image

But then in various proposals you see things indicating that yes, it is in group "2".

@wbamberg Thoughts?

dy commented 2 years ago

In this case according to standard ?: ternary is also not operator but conditional assignment...

hamishwillee commented 2 years ago

Possibly. I'm deferring to the experts - @sideshowbarker , @wbamberg do you have an opinion. I'd be happy to add this myself.

sideshowbarker commented 2 years ago

Maybe worth adding => operator precedence? It seems to be 2 - assignment group. TC39 members often use that precedence as argument.

Not sure. There is this note in the docs image

Yeah, => is not an operator, so it doesn’t belong in the Operator Precedence table. Similar to how spread syntax is not an operator.

In this case according to standard ?: ternary is also not operator but conditional assignment...

In general, anything existing currently in that table that’s not an operator should be removed from the table — and certainly shouldn’t be used as a precedent for adding any other no-operators to the table.

dy commented 2 years ago

I'm concerned that removing operators from the table makes it less useful. Standard is needed for implementors, but what comes to users - it's very helpful to learn when parens are needed in case of => or ?: (which is conventionally called operator, regardless of particular name in spec). Afterall they account for precedence (unlike statements), eg. it's important to know that () => a, b means (() => a), b and not () => (a, b), because => has higher precedence than ,

hamishwillee commented 2 years ago

@sideshowbarker I think @dy makes a valid point - if you're a user you want to know the precedence of particular "thingies" (a technical term we use in Australia) whether or not the thingies are technically operators. I personally did not know they were not operators and would search here. There is nowhere else it would occur to me to search. That said, it is important MDN does not incorrectly refer to something as an operator that is not - we're supposed to be adding clarity :-).

So we can remove them but we need to provide some excellent and obvious linking to where the information is provided. OR we can keep them in the table, but make it very clear that some subset are not actually operators.

Thoughts?

Josh-Cena commented 2 years ago

For the purpose of a reference page, I'm also pro-including it. Maybe the page title doesn't accurately reflect its content in this case, but it would still be useful to know how tightly a particular construct—even ...—binds to expressions/identifiers. Cross-referencing https://github.com/mdn/content/issues/12916#issuecomment-1036553740:

The operators at this level don't really have more or less "precedence" than one another; they interact under special rules. See the following examples.

We should either include =>, or exclude =/./etc. (but mention them in an extension section), as IMO they operate with very similar mechanism: they don't allow arbitrary expressions as both operands, but are part of special syntaxes.

sideshowbarker commented 2 years ago

For the purpose of a reference page, I'm also pro-including it. Maybe the page title doesn't accurately reflect its content in this case, but it would still be useful to know how tightly a particular construct—even ...—binds to expressions/identifiers.

We could re-title (and redirect) the page. I guess we should.

But then that presents the different problem of what title to give to that page. Back when I was thinking about this, I think one thing that came to mind was that some of the things people wanted to include in the table had to do with the language’s rules for order of evaluation — rather than operator precedence.

So I dunno, maybe we could title the page “Operator precedence and order of evaluation” — which is somewhat ambiguous because it can be misread as being about operator order of evaluation, rather than “operator precedence” and “order of evaluation” being separate things (and I hope we have agreement that in general in languages they are two separate things…). But anyway, I don’t have a suggestion for how to make it both unambiguous and reasonably short.

Josh-Cena commented 2 years ago

I dunno either, to be honest... Originally I thought we should remove "assignment" as well since those don't really have the idea of "precedence" (they can't associate arbitrary expressions on LHS and RHS). But if we keep removing operators (/ constructs) we just make the page less useful without any gain of utility. Maybe we can make an additional section about how the non-operator constructs compare in binding tightness with those that are operators.

js-choi commented 2 years ago

I mentioned this last year in #5365, and I’ve meant to open a pull request about this for a long time. (I think we’ve talked about it sometimes on the TC39 Matrix. I’ll try to find logs when I can.)

Anyways, my opinion remains the same:

=> is an operator in every way that = is an operator. If you consider = to be an operator, then I can think of no* good reason why => is not also an operator:

Compare [a] + f(), [a] = f(), and ([a]) => f(). In the = and => cases, only their RHSes (and not their LHSes) are expression operands. (We could quibble whether the LHS bindings should also count as “operands”, but it’s undeniable that they are not operands in the same way that +’s LHS is an operand.)

I think it’s completely appropriate to include => in any “table of operators” for JavaScript if it also contains =. I wouldn’t be against renaming it per se to make it more descriptive, but adding => to the table should not affect the decision to rename it that much.

(As an aside, one might say that it’s a misnomer to call = (also =>) a “binary” operator, since only its RHS is an ordinary expression. It’s more like a unary prefix operator with a funny extra LHS. In [a] = b + c, the b + c is an expression, but the [a] is not an expression. Though this is complicated by the fact that the LHS can contain default expressions like in [a = f()] = b + c, in which case the f() is an expression too…)

 Of course, there is one important difference: unlike =, => accepts either expressions or blocks as its RHS. So maybe ([a]) => f() is an operation on f(), but what about ([a]) => {}? I’m less certain about this. I feel like the {} block ought to be considered the “operand” of =>. There are plenty of operators with special rules, and operands don’t have to be restricted to ordinary expressions. The crucial part is that => definitely is still creating an expression, so if not an operator it is operator-like. This is still very different from, say, the spread syntax ..., which does not* create an expression.


[Edit: Pasting here a response to a point from @sideshowbarker on Matrix that we could exclude both = and =>]

It would certainly would be consistent to exclude both = and => from being operators. But 
I don’t know if there’s any rigorous-but-still-useful definition of “operator” that would exclude =.

I think that you could rigorously define a JavaScript operator as a “syntactic token(s) that create an expression but which is not an atomic literal”, where an “expression” is a “syntax phrase that evaluates into a value at runtime”.


So 33 is not an operator. But yield is a nullary-or-prefix operator (with side effects). + is a prefix-or-binary operator. import() is a circumfix operator. And = and =>, I think, can reasonably be rigorously called operators, too. They’re certainly not atomic literals or statements.

In contrast, the ... spread syntax of array/object literals, the inner = in [x = 0] = y, and the match proposal’s ${ } interpolation syntax would not be operators, because they themselves do not create expressions.

(See also prior TC39 discussion sparked by the ${} syntax of match: 2021-12-08, 2021-12-08)

Josh-Cena commented 2 years ago

I agree with the point here. At some point when I was trying to rephrase that page, I was tempted to define operators as "joiner of arbitrary expressions", in which case neither => and = are operators, because their LHS are bindings, not any expressions. But then the definition loses its point. I also did a poll in the TypeScript Discord server, and funnily got a unanimous result that = is an operator but => is not... In any case, I think in the case of a => a + 1, I would definitely say the "precedence" of => would be useful to the reader.