feat: Generalize type of step indices with metaprogramming and typeclasses - #576
Open
markusdemedeiros wants to merge 37 commits into
Open
feat: Generalize type of step indices with metaprogramming and typeclasses #576markusdemedeiros wants to merge 37 commits into
markusdemedeiros wants to merge 37 commits into
Conversation
… for `Nat` in `StepIndexFinite.lean`
…stances not yet complete
Replace all proofs in section `Fixpoint`, all requires rewrite
…and `Contractive.succ`
Parametrisation of `CMRA` to be done in a future PR
…nt with `SI = Nat`
Collaborator
Author
|
Not for nothing: a similar design might be capable of removing the |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR builds off of Alvin's work in #550, as well as Mario's work and my own attempt. I believe it solves every single problem reported with the prior attempts at this feature. The text here is long because I will try to explain how.
TL;DR:
outParambehaviors around local class inference.OFE.eq_dist: see limitations below).SIdxclasses even in the same theorem.In PR #550 I mentioned that having local scoped instances that could depend on section variables would solve all of our problems. This PR started by trying to emulate that, and morphed into something I think might make everybody 99%-100% happy.
Technique
The trick is to have a special typeclasss for default
SIdximplementations, and let that provide the default instance forSIdx. Concretely, we start with the normal implicit index version of OFE (similar for COFE, OFunctor, etc)however, we add a second class for declaring default step index instances with an
outParamindex, and let it be the default instance for the step index typeInitially I did this to put different defaults in different scopes (so we could emulate scoped default instances) but I ended up deciding that an elab was a better way to control it. To that end, the
stepindexcommand puts an instance of theDefaultSIclass in scope and does some sanity checks to ensure that it is the only one at elab time:scoped stepindex NatOrdinal works in IrisMath too. Unlike hypothetical scoped default instances, the default index type can depend on section variables, so your type of step indices can be defined from any other Lean machinery. Here is how to generalize a section to use a generic type of step indices, such that the
SIdxconstraint on the type obeys normal typeclass synthesis rules:Hierarchy
Once a
stepindexis declared, aDefaultSIinstance is put in scope, so plain typeclass synthesis will handle filling in theSItype and the[SIdx SI]instances almost exactly like it would have in theoutParamapproach. However there are big improvements related to how stable this is. Synthesis of this arbitrary instance happens once, at the time thelocal stepindexcommand is elab'd, and in the happy path this instance will be synthesized using normal synthesis rules (ie. it will pick the[SIdx SI]instance forSIin the snippet above, not an arbitrary instance at every call site). Even if you get it wrong, and break the hierarchy in some other way, because synthesis happens only once this choice is guaranteed to be self-consistent for the rest of the section.I've also used the module system to make declaring
DefaultSIinstances out of band difficult. It's not impossible, I don't think, but I think it is very hard to do by accident. Compare this to accidentally copy-pasting anopenstatement that includes a scope you didn't realize had aSIdxinstance in it: the latter is much easier to get wrong. Setting a global step index type is also disabled.Basic hierarchy discipline like avoiding non-definitional diamonds still applies of course, but it is no longer possible for an unrelated
SIdxtypeclass instance to break unrelatedOFEsynthesis. Thestepindexcommand also has an option to provide an explicit instance name: I suspect this is unnecessary, but it gives you to turn off even the command elab-time nondeterminism if such a need arises. The final hierarchy thing I'll mention is that, unlike theoutParamappraoches, the default step indices are fully opt-in. I can useOFE/COFE/OFunctor/... without interacting setting default step indices at all, and it behaves like any other two-parameter Lean typeclass. You can use multiple index types in the same theorem if you want. The stability of our algebraic hierarchy is preserved with or without default step indices set.Notations
Because of the
outParam, you get nearly every nicety of Alvin's original approaches, and adapting old code is nearly zero work.OFEdoes not need to specify itsSIparameter, and step indices (even0) do not need to specify their type. I also reused thestepindex%term elab from my other attempt: this elaborates to the current default step index type or a hole if none is set. However, unlike my prior approach, this is not used for any oddoptParamstuff: it's used a total of three times so that we can have reuse the old notation and make it fill in theSItype at its elab site:Doing it this way avoids the need for the duplicate scoped notations as in Mario's version. I didn't even define notation for adding in the type
SIbecause I think at this point it is not necessary.Limitation
The only edge case I could not resolve was for
eq_dist(and a few one-offs like it): this is an annoying combination of both not being a notation, and not referencing the step index type, so neither typeclass inference nor anoptParamcan be used to fill the type. Admittedly, theoutParamversion (nondetermistically) fills this in at every call site where mind doesn't.In this PR I explicitly fixed each
SIinstance beNatorSIas at each call site so that the Lean code looks normal. I could have also definedeq_dist'to beeq_distfixing(SI := stepindex%)with anoptParamand it would be uniform across the repo. I'm open to any solutions to this problem.Conclusion
I hope this version meets everybody's requirements! I think this solution is more than the sum of its parts, and cards on the table, I'm very pleased with it. To me it feels quite inline with other Lean features. I look forward to your feedback :)
cc: @alvinylt @MackieLoeffel @Kaptch @digama0