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
Meaning-preserving parsing rules for SCC annotations #176
Conversation
413d6e1
to
af442f6
Compare
Well argued! |
af442f6
to
d297d42
Compare
I am all for this change. The primary reason is that the syntax of annotations, I assume, intentionally mimics the syntax of comments by reusing Regarding assigning the meaning to an annotation, i.e. what its “scope” should be, I think the simplest to explain to someone behaviour would be to parse an annotation as an atom that is being “applied” to an expression by the means of function application, following all of its rules. I feel that this treatment would be very straightforward and uniform. So:
Nevertheless, if you imagine a parse tree with function applications represented as binary operators and annotations as “unary” nodes, it is safe to “pull” an annotation node up as long as it is in the left subtree (the important part here is that it is not allowed to change the structure of the tree in any other way). If one pulls it all the way up, the resulting behaviour will be similar to the current one (extending to the right as much as possible) with the difference that “as much as possible” here respects the restriction that the meaning of the expression should not change. This way:
One other option is to allow “pulling up” only through function application nodes, although, to be honest, I think this would be somewhat arbitrary. So that:
My personal favourite is the first option, because, even though it will require sprinkling parens here and there, it is the most straightforward one and it allows me to quickly see what subexpression an annotation applies to without thinking too much about operator fixities and precedences. |
@kirelagin That's a good observation, I added it to the proposal.
If we follow all of its rules, it means that
and that clearly won't fly. Another complication is that if we treat the annotation as an atomic expression, unary
And since function application is left-associative, nested annotations would be applied to each other, but we'd rather have them right-associative and both apply to the subexpression:
There's a technical complication here if we want to pull through operators, as we know what tree is the "left" subtree only after renaming, as it depends on operator fixity. Consider the following example:
From the implementation standpoint, it would be preferable to resolve what annotations apply to in the parser, before fixity information is available. |
For those of us who didn't know what SCC stood for before coming across this proposal ... Yes @kirelagin makes a good point: pragmas look like comments, and we expect they shouldn't have an effect on syntax. But ... GHC is moving away from global/per-module For example, per-instance There's proposals in the pipeline for per-class pragmas. So my intuition with other pragmas is you put them just inside the start of a syntactic construct, and they scope over the whole body (up to the closing
If you put a pragma anywhere else in those decls, it's rejected/ignored. And the pragma is not binding tight as-if applying a function. It skips over the context and means the whole Following that intuition, it seems to me the first here should be rejected, in favour of one of the others
I think that trying to give pragmas associativity or fixity is just too hard (for human readers, even if @int-index can persuade yacc to turn somersaults). So please let's not restrict this proposal narrowly to SCC pragmas. Let's state a general rule that applies for all pragmas, then see how much SCCs already comply. |
heh heh. GHC seems to delight in being inconsistent with pragmas.
|
@AntC2 I agree. To be clear, that is what is being proposed currently. My plan is to move the productions for parsing these annotations into the |
Good. I'm not sure that's clear from the page. "Disallow ... annotations in positions where they may affect the structure of an expression." seems to raise questions like: what structure? "affect" how? I don't think there's much need to talk about "structure", "precedence", "placement". And
Seems to be talking to yacc rather than yakking to humans. Can we say:
|
I like this proposal, provided we can explain clearly, succinctly, and unambiguously to users where they can put an SCC pragma. @int-index can you propose some text for the user guide? It would also be helpful to see exactly what changes to the grammar would be required. |
This is a breaking change which should be mentioned at least in the implementation plan. I don't know what the policy for introducing breaking changes in annotations is, but I guess we'll have to first generate a warning and only after a few releases disallow annotations that change parsing of expressions. One other thing that would also be helpful to define "structure preservation" more formally. I think it means that if I have a parse tree
this is not a structure-preserving annotation:
but I don't know how to define this formally so that for any addition of Finally as @simonmar also asked, I'm wondering what changes in the grammar/parser will be required. Do we only make some changes in the application grammar/parser to disallow annotations before args or do we need more changes in other contexts? |
@osa1 We don't have to do it – all programs that compile with the proposed change also compile without it, so the users can update their code and keep support for older compiler versions without
The issue with this definition is that the parse trees are not the same. They differ in a specific way – When I tried to put it into words, I ended up with the phrasing currently used in the proposal:
where "structure" refers to the syntactic structure (i.e. the parse tree), and "meaning" is a redundant clarification. I think it's formal enough.
@simonmar We take these productions:
and move them to the To achieve good error messages, we also add
It's tempting to write the rules similar to @AntC2's interpretation in #176 (comment):
However, it does not cover
and that's probably incomplete. I think the only true way to say what we want to say is to name two principles:
|
I think you meant "spans to its end", but that's not enough: e.g. this: Perhaps you want something like "An SCC annotation can occur anywhere that you could put |
What about the following specification?
|
All that reminds me of @int-index your proposal in the previous comment should note that those three in fact do bind exactly as loosely as those annotations. It should be noted somewhere that all of them are mutually right associative, even if that is implied by the meta rule (I'm not sure!). |
@Ericson2314 We can probably allow The rule I wrote takes this into account. Do you have an alternative rule that takes care of all corner cases? |
So for reference the Haskell report says:
Agreed, this relates to that meta rule not mentioning
I want to make clear that the following are improper parses: \ x -> f {-# SCC ann #-} y ==> (\ x -> f) ({-# SCC ann #-} y)
{-# SCC ann #-} y \ x -> f ==> ({-# SCC ann #-} y) (\ x -> f) I also view the "extends as far to the right as possible." language of the Haskell report as insufficiently formal. I like your phrasing better because I agree that "precedence" applies to production rules in general, not just infix operators. (I view preference as competition to be the root/parent parse tree node, the "looser" rule "wins".) Given the precedence way of formalizing things, I think:
I'm not quite, sure but it could be that that possibility would prefer the parse: {-# SCC ann #-} y \ x -> f ==> ({-# SCC ann #-} y) (\ x -> f) over {-# SCC ann #-} y \ x -> f ==> {-# SCC ann #-} (y (\ x -> f)) because in the former the lambda node is closer to the root than the latter compared to the SCC node. So given all that what I propose is
|
@Ericson2314 This sounds reasonable to me. In terms of grammar, it means that in addition to parsing
I will add your wording to the proposal. |
Great, thanks! |
@nomeata I'd like to submit this proposal to the committee. |
Sorry for the delay; this got accepted. |
There's a prototype implementation at https://gitlab.haskell.org/ghc/ghc/merge_requests/860 now. |
The proposal has been accepted; the following discussion is mostly of historic interest.
In today's Haskell adding an
SCC
annotation can change the semantics of an expression:This is a side-effect of the parsing rules for
SCC
annotations. We propose to change the parsing rules in a way that guarantees that adding an annotation does not affect the meaning of a program.Rendered