Only accept SELF on the left for left-associative levels#21126
Conversation
|
I completely broke associativity... i.e. |
|
Closing this draft because @proux01 fixed it, see #21072 (comment). |
|
Actually I don't think this was buggy (it passes the same tests as the commit from @proux01). The
Level 2 written like that makes no sense. It essentially means |
|
Indeed, this seems to work, let's reopen. |
I don't know about the warnings. I already don't find them correct on master. Regarding the diff between both commits, in my opinion the biggest difference is the approach. In your case you reject a recovery on a wrong level after running the suffix tree, while in my case I restrict the range of levels to continue which won't even try running the suffix tree for the wrong level if Another minor (but semantically significant in my opinion) difference is that you check
Should I give it a go? (I'm not convinced I should produce a PR if there's too much OCaml involved.) |
Maybe the wording could be improved? These warnings are kinda important since they are the main tool for users to adapt to the future change.
No opinion on which implem is better. I just think that it is important for the warning to be precisely located (in this case, indicating at which character position the user can add parentheses).
Indeed, you're right
Feel free to
Well, this repo is essentially OCaml code, so PRs are essentially OCaml. |
Yes I agree they are important. The problem I'm referring to is not really the formulation, but the levels (and maybe sometime the location? I'll need to find examples). An example that I find confusing for levels is: Check 0 > ~ 0.It gives the following warning (on the correct location of I would expect |
|
Honestly, in my experience as a user, whereas the location is super important, the levels in the warning message aren't very useful: either you decide to add parentheses and you don't care about the levels, or you decide to change some levels which means you need to find where the notations are reserved and you'll then also get their levels. So maybe our best move would just be to get rid of the levels in the message. |
|
I see, makes sense. |
C.f. #21166 |
|
Rebased to fix the conflict I created with #21166 FTR, this was discussed during yesterday's call, there was a general agreeement we should do it, with the following requests:
|
Adding parentheses is not always guaranteed to work, in particular custom entries don't have to have a rule with parentheses embedding the top level into the bottom level. |
|
Yes, it's more a best effort that will work in many cases than a 100% foolproof solution. |
|
I've rebased onto master and split into 2 commits: one adding the test file and one with the actual change (which I changed to use the level-tolerance warning such that the error locations are correct). In particular, the |
proux01
left a comment
There was a problem hiding this comment.
I think it would be nice to have an indication in the message on whether the issue lies on the left (all warnings prior to the current PR) or on the right (the new warnings from this PR) of the flagged location.
Otherwise LGTM, we still need before merging (potentially in separate PR) to:
- fix the ssreflect
_ => _issue (I'm looking at it) - try to improve the recovery locations so that we can provide a quickfix suggesting adding parentheses (maybe only on constr were we are sure the parentheses do exist)
- get an extra review
| > Check Reject 10 [2 atom3 ]. | ||
| > ^^^^^^^ |
There was a problem hiding this comment.
Definitely independent from the current PR but here, I'd rather expect the following location
| > Check Reject 10 [2 atom3 ]. | |
| > ^^^^^^^ | |
| > Check Reject 10 [2 atom3 ]. | |
| > ^^^^^ |
not sure if this could be reasonably obtained.
There was a problem hiding this comment.
Yes, I was also expecting that more narrow error location.
SkySkimmer
left a comment
There was a problem hiding this comment.
This creates a warning in the ltac2/custom_entry test. Ltac2 custom entries declare levels to be RIGHTA so that self on the right and on the left always means the current level and leaves associativity to the rule writer, this PR would break that.
IDK if there should be a new associativity that doesn't warn or what but this shouldn't be merged as-is.
This is not what right-associativity means. Right-associativity means that
What you want would be all-associativity defined by
Note that all-associativity is ambiguous if no care is taken. The user needs to write their rules (e.g. notations) such that no ambiguity is possible, i.e. I don't think this would be difficult to implement once we have add the ALLA variant to NONEA, LEFTA, and RIGHTA. However, I wonder how niche this is. Do we have concrete examples of users that need to control associativity for each rule rather than each level? The examples in |
My understanding of this hack is that you use RIGHTA as a way of trying to convince gramlib to "stop trying to handle associativity for me". Note that the ultimate goal of this PR is to ultimately get rid of any associativity notion in gramlib (and have higher levels be the only one to handle it, through proper use of SELF and NEXT). Unfortunately, note that this doesn't perfectly work, when you write (* we can also do right associative notations *)
Ltac2 Notation x(self) "-" y(self) : mycustom(1) := (x, y).what you'd really like to write is rather (* we can also do right associative notations *)
Ltac2 Notation x(next) "-" y(self) : mycustom(1) := (x, y).but unfortunately this doesn't quite work as the parser doesn't handle it since all levels are righta and you need to write Ltac2 Notation x(next) "-" y(self) : mycustom(1) := (x, y).
Ltac2 Notation x(next) : mycustom(1) := x.Not sure what's the best solution. Maybe we should just allocate "correct" associativities rather than the default righta for now (this would restrain a tiny bit the expressivity of ltac2 custom entries (at least as long as gramlib insists on managing them) but probably nothing unacceptable) |
|
I think there's some confusion due to the meaning of SELF and NEXT. There are 3 different meanings:
I've been using SELF and NEXT with this last meaning, because that's what matters (and that's what SELF means in the title of this PR). Ideally we could make the Rocq syntax match the Gramlib semantics. But we won't be able to make the Gramlib syntax match its semantics without major changes because of the strong distinction between start and continue trees (which hardcodes the meaning of
I would say this PR does actually the complete opposite of that. Also this seems to contradict one of your previous comment:
So maybe we should try to go back to the blackboard and see what we want to do. I see at least 2 toplevel options:
I believe both are possible (ignoring the amount of effort), so the question is what we want. I'll try to give pros and cons, but I'm sure this is non-exhaustive. Pros of level-based associativity (thus cons of rule-based associativity):
Pros of rule-based associativity (thus cons of level-based associativity):
Ignoring difficulty and sunk-cost fallacy, rule-based associativity seems better. This would align with what both @SkySkimmer and @proux01 seemed to prefer in their last comment. If we want to go that direction, then indeed this PR is completely useless, because we will remove NONEA, LEFTA, and RIGHTA altogether. All levels will have all-associativity (i.e. syntactic SELF on either end means SELF and it's up to the user to avoid ambiguities). I don't know if I will be able to implement the change to Gramlib but I could take a look if that's the decision. (That would be a much more ambitious project than just fixing Gramlib as this PR is doing.) |
|
The term "associative" seems like it's probably misleading. The last time I looked at the parser a couple years ago, there was no way that |
I believe this always worked: Notation "x 'op' y" := (x, y) (at level 60, only parsing, right associativity).
Check I op I op I.
(* (I, (I, I)) : True * (True * True) *)However, I agree that we use the word associative in a more general meaning than just binary operators. We use associative to describe the meaning of SELF on the left and on the right, regardless of the symbols in-between (there could be 0, 1, or more). |
|
Well, the current PR is essentially just introducing a deprecation warning. About level/rule based: IIRC, the major argument in favor of level based associativity was to reduce the risk of surprising behavior (what happens when mixing left and right associative notations at the same level). I'd say the major argument for rule based would be increased compatibility between libraries (no more dreaded error "level n is left-assoc in library A and right-assoc in library B"). In any case, I tend to think that interface/user facing arguments should come first before implementation cost.
wrong |
I'm not able to follow this step. I don't see why we need strict associativity (this PR after deprecation) to get rid of associativities in gramlib. It seems to me we could just straight get rid of them now.
Ok, then if I find time this week-end I'll try to add the
Ah ok, Ltac2 custom entries is something new. That's why I couldn't find anything in the documentation. I was trying
Ok so it really seems rule-based is better, because it should be possible to avoid surprising behavior with a good syntax to describe production rules.
I totally agree. |
Don't we need to temporarily keep the associativities, at the very least to trigger the warning?
Maybe we should also keep a warning like "using left associativity on level xyz previously only used for right associativity" to limit the risk of confusing behavior? But no strong opinion yet. |
I'm not sure which warning you're talking about. I'm assuming "we can get rid of associativities in gramlib" means we only implement associativities at Rocq-level. To do this, we just need to make every level RIGHTA because the current semantics of RIGHTA is buggy (this PR is fixing it). It has the semantics of FULLA/ALLA which is the one you want if you had to choose only one (which is equivalent to having both associativities), because that's the most permissive and from that one you can either implement left-associativity or right-associativity (but not no-associativity, see below). That's essentially what ltac2 custom entries is doing. So this PR is not needed for that (actually this PR is bad for that, because it fixes RIGHTA to only be right-associativity and not all-associativity). Actually, only using RIGHTA is not enough. Gramlib needs to be extended to support annotations when a continuation is after SELF or NEXT. It would be like having 3 trees instead of 2:
Otherwise we still have the bug that right-associativity is all-associativity and no-associativity is left-associativity. This change would actually be the change needed to get rid of associativities in Gramlib (thus have rule-based associativities in Gramlib).
Thanks, I forgot about that. I don't know why company-coq doesn't complete to it.
Yes, we'll most probably need some warnings for the transition. But it's not clear to me yet how this is going to happen. I'll need to learn all the quirks of the translation from Rocq notations to Gramlib syntax. |
|
I've pushed a commit that adds BothA (called multi-associativity) that behaves like RightA before this PR, such that this PR can fix RightA to be right-associativity. I'm also making ltac2 custom entries use BothA instead of RightA, thus restoring the current behavior (no warning). Also I'm using |
This is very conservative, only when we know the end location is correct and only when we know the entry [ename] has parentheses. This should still cover most practical use cases.
|
@coqbot merge now |
|
@proux01: You cannot merge this PR because:
|
|
@coqbot merge now |
|
@proux01: Please take care of the following overlays:
|
This PR is a follow-up of the discussion in #21072, initially started by #21029. This can also be seen as the continuation of the work started in #17876. In particular, this PR provides proper support for non-associativity and fixes leniency in right-associativity (namely that the left component recognizes the non-recursive part of the same level instead of the next level only).
Depends on: #21244(merged)Overlays (to be merged before the current PR)
Overlays (to be merged in sync with the current PR)