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
Provide better clarity around operator precedence and associativity #4332
Conversation
Visit the preview URL for this PR (updated for commit e6ed24c): https://dart-dev--pr4332-feature-document-ass-ys8cfbyn.web.app (expires Tue, 15 Nov 2022 01:48:10 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 Sign: d851bc446d3c4d7394c5406c6f07255afc7075f3 |
@eernstg What do you think about these clarifications? I know we want to avoid highlighting this table as outlining operator precedence as the concept has some flaws, but users seem to be finding alternate sites which do not highlight this concern when searching on Google. I tried to make the table a bit more searchable in terms of this, while also making the warning more clear. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! I suggested adding an example showing how the grammar can give rise to an operator precedence relationship.
The operator precedence outlined here | ||
is only an approximation of the precedence and associativity | ||
given implicitly by Dart's grammar | ||
and should only be used as a helpful guide. | ||
For definitive answers, consult the grammar in the | ||
[Dart language specification][]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: Why does this only approximate precedence? Is there a difficulty in providing the specific precedence? What is that difficulty?
suggestion: Tighten the language and use active voice.
The operator precedence outlined here | |
is only an approximation of the precedence and associativity | |
given implicitly by Dart's grammar | |
and should only be used as a helpful guide. | |
For definitive answers, consult the grammar in the | |
[Dart language specification][]. | |
The previous table provides a rough guide to operator precedence. | |
You can find the authoritative precedence and associativity for Dart | |
in the [Dart language specification][]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not that you can find any authoritative precedence and associativity in the language specification as they aren't really defined (or able to be exactly), but rather you need to look at the whole grammar to understand the relationships between operators.
I've took some of your changes and Erik's, while also being clear about it only be used as a guide (which was the source of confusion that caused the introduction of the note a long time ago).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could comment a bit more on why operator precedence is not a precise concept.
If you settle on a specific class of parsers (say, table driven LALR(1) parsers) then you can have a technically precise notion of precedence. In case of an ambiguity, the parser will then use the assigned precedence values to make the decision about whether to shift or reduce, and which rule to reduce. This makes sense if we have a grammar where a lot of different expressions use the same nonterminal:
...
expr ::= expr ('+' | '-') expr @precedence(5)
expr ::= expr ('*' | '/') expr @precedence(6)
...
Precedence has a precise meaning in this context, and it is a tool outside the underlying context free grammar formalism (it is defined in terms of the chosen parser algorithm, it's not a property of the grammar — the grammar is simply ambiguous).
If we are using a different approach to parsing (for instance, Dart has always used an enhanced recursive descent parser, which is more like ANTLR) then there is no reason to assume that there exists any assignment of precedence to any set of operators that will describe the behavior of the parser correctly. In other words, the whole concept of precedence doesn't necessarily fit that approach to parsing. When using that approach, the ambiguities simply do not arise, because we use different nonterminals to establish a similar property:
...
additiveExpression ::= multiplicativeExpression (('+' | '-') multiplicativeExpression)*
multiplicativeExpression ::= someOtherExpression (('*' | '/') someOtherExpression)*
...
Looking at the grammar you can see that an additive expression can consist of a sequence of multiplicative expressions, separated by '+' or '-' (and not the other way around), and that makes it tempting to say that '*' has a higher precedence than '+'. (Of course, there would be more rules about additiveExpression
, because it can also derive other things than a sum of products, but this is the core part with respect to precedence.)
However, the source of truth is the grammar, and if we settle on a particular set of precedence values, it takes a non-trivial proof to verify that this precedence table describes the derivable programs with that grammar precisely (and maybe that proof doesn't exist, because it just isn't true).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@atsansone Any suggestions on how to make it tighter? I'm having trouble focusing it even more while still answering the three questions and concerns commonly raised (in the linked issue, past issues, in the language repo about this table, and by Erik):
- How it should only be used as a "helpful guide" not definitive
- Briefly explaining why it can only be used as a helpful guide
- Where/how to find and understand the authoritative behavior
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@parlough : I have a few comments for your review.
c888fee
to
6db681a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM % 1 suggestion.
Dart supports the operators shown in the following table. | ||
The table shows Dart's operator associativity | ||
and [operator precedence](#operator-precedence-example) from highest to lowest, | ||
which are an **approximation** of Dart's operator relationships. | ||
You can implement many of these [operators as class members](#_operators). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: To tighten language, sometimes you need to write more, shorter sentences.
Dart supports the operators shown in the following table. | |
The table shows Dart's operator associativity | |
and [operator precedence](#operator-precedence-example) from highest to lowest, | |
which are an **approximation** of Dart's operator relationships. | |
You can implement many of these [operators as class members](#_operators). | |
The following table lists the operators that Dart supports. | |
The table sorts the operators from highest to lowest precedence. | |
When determining which operator to apply first, Dart compares operators. | |
When comparing two different operators, | |
Dart applies the operator with greater precedence first. | |
When comparing two of the same operator, | |
Dart applies the operator from the associative direction first. | |
Complex expressions may apply operators in a recursive fashion. | |
Due to the complexity of precedence, keep your expressions short, | |
store stages in varables, then chain those variables into further | |
expressions. | |
Consider this table a guideline only. | |
To learn more about the specifics, refer to the [Dart grammar](https://github.com/dart-lang/sdk/blob/main/tools/spec_parser/Dart.g). | |
You can use many of these operators as class members. |
- How it should only be used as a "helpful guide" not definitive
- Briefly explaining why it can only be used as a helpful guide
- Where/how to find and understand the authoritative behavior
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't help commenting here! ;-)
This doesn't tell the reader that the whole concept of operator precedence is imprecise. It's not a matter of complexity, there's just no reason to assume that there exists an operator precedence table which is a correct description of the behavior of a Dart parser. It would certainly be a non-trivial effort to establish any firm results in this area.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm also a bit worried about providing so much information in the opening here, when most readers are just looking for the table. We expand on the the precedence right after the table already, and I like your comments around that, so I'll try to incorporate some of that there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We talked about it, this makes sense, thanks!
While the precedence outlined by the table should only be used as a guideline, users are instead finding third-party sources when googling which do not highlight potential issues and are not able to identify/search that this table still approximates the behavior even if they navigate to the language tour.
This PR makes the table more searchable and identifiable as outlining the approximate precedence, while also clarifying the warning which was a bit confusing, focusing on that it can just be used as a "helpful guide". Beyond that, this adds the associativity labels present on the language spec's "helpful guide".
Fixes #4287
Staged: https://dart-dev--pr4332-feature-document-ass-ys8cfbyn.web.app/guides/language/language-tour#operators