feat: inext with later credits - #510
Conversation
|
The latest improvement is to use the same trick for HeapLang tactics for matching |
| inext 4 credit: Hcred | ||
| iassumption | ||
|
|
||
| /- TODO: in the Rocq version, `intoLaterN_later` fails to apply, and `intoLaterN_laterN_bool` applies instead -/ |
There was a problem hiding this comment.
This is a test that is supposed to use intoLaterN_laterN_bool. This instance is not yet ported, so I have done so along with the addition of this test.
As mentioned in a comment in the Rocq version, it might cancel a later "too much" in the worst case, so this instance has a lower priority than intoLaterN_later and intoLaterN_laterN.
In the Rocq version, neither into_laterN_later nor into_laterN_laterN applies to this test, so into_laterN_laterN_bool applies, and we get H : P. In contrast, BIBase.laterIf is a reducible def in Lean, so ▷?p P is equivalent to laterN (Bool.toNat p) P. As a result, into_laterN_later applies (given it has a higher priority than into_laterN_laterN_bool).
I'm not sure whether this is intentional or a bug: MaybeIntoLaterN is explicitly ignored and, unlike the Rocq version, intoLaterN_later and intoLaterN_laterN does not check progress using TCIf and TCEq. Is there an intended workaround for this, or should we have ported MaybeIntoLaterN and implemented the instances faithfully?
There was a problem hiding this comment.
In commit 0b7d5fb, I tried to implement the design in Rocq faithfully with TCIf introduced into Std/TC.lean and MaybeIntoLaterN being a type class. Basically, IntoLaterN is the type class that necessitates progress, whereas MaybeIntoLaterN allows a fallback option that enables propositions to remain unchanged. This is intended to avoid the lack of progress being silently accepted without trying other instances. In the example test above, the intended behaviour should be:
- The instance
intoLaterN_laterhas a higher priority and tried first. - No progress is made with
intoLaterN_later, instead of leaving theHunchanged, backtrack and try other instances. - The instance
intoLaterN_laterN_boolis tried. Progress is made, so the result of the synthesis is adopted.
This approach with the use of TCIf, however, broke the tactic as it is not an IPM type class (because it is meant to be in Std/TC.lean). With reference to the type class synthesis trace, the built-in type class synthesiser in Lean tries the applicable instances of IntoLaterN. If they all fail, the fallback option with MaybeIntoLaterN applies, and the TCIf ... instance overall succeeds. And when this propagates to the recursive type class search caller, it uses this default result of unchanged proposition directly. The search could possibly have backtracked such the recursive search had a non-default result.
In fact, there seems to be some trick for TCIf in Rocq's stdpp to avoid this:
Global Hint Extern 0 (TCIf ⇒
first [notypeclasses refine (TCIf_true _ _ _ _ _); [tc_solve|]
|notypeclasses refine (TCIf_false _ _ _ _)] : typeclass_instances.So the solution is similar: instead of using TCIf, we define a wrapper type class GuardedIntoLaterN which is an IPM type class. Its two instances (guardedIntoLaterN_stuck and guardedIntoLaterN_progress) are not labelled with ipm_backtrack. Meanwhile, instances of IntoLaterN and MaybeIntoLaterN are labelled with ipm_backtrack and adjusted accordingly.
|
Now trying to implement |
…nd then `intoLaterN_laterN_bool`
|
The type classes The tactic instance See diff here. |
|
To be honest this new approach isn't really that simpler compared to the original port, especially with all the wrestling with Qq proofs, but is (hopefully) nevertheless easier to understand. Commit: 4010144 |
Description
Implements the tactic
inext n credit: H.Addresses #239.
Similar to the Rocq implementation, once all credits are consumed (that is,
Hbecomes£ 0), the hypothesis is discarded.The type classUsingAddModaland its instances have already been ported in PR #470, but are nonetheless also included in this PR so that we can use them for this tactic.ElimModalinstead.This tactic is unique in the sense that it is relevant only forIProp GFwhereInvGS GFholds. Within theProofModeM.runTacticblock, we have the generic BI instanceprop. Otherwise, if we useisDefEqto unifypropandInvGS GFmanually, we still run into universal level problems:In the implementation, we useResolved in commits 9056733 and 9b1963a.mkAppMso that the instanceInvGS GFis synthesised automatically.Checklist
authorssection of any appropriate files