-
Notifications
You must be signed in to change notification settings - Fork 463
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
RFC: well-founded recursion: automatic .attach
insertion
#5471
Comments
#5477 hopefully fulfils the cleanup requirements. |
There is a problem with this plan: The rewrites done by the processing do not preserve definitional equality. This means that often the rewriting will fail. For example rewriting
to
is likely bad, because the proof in |
How bad do things become if |
You mean as part of the translation, or completely everywhere in Lean? |
The latter (letting what is now |
I expect that that’s going to be quite horrible. For example it will make it hard for |
On the other hand, it would be great to solve that problem anyway for the current |
Just saw this code scroll by on zulip, noting here as a test case for this feature
|
This PR extend the preprocessing of well-founded recursive definitions to bring assumptions like `h✝ : x ∈ xs` into scope automatically. This fixes #5471, and follows (roughly) the design written there. See the module docs at `src/Lean/Elab/PreDefinition/WF/AutoAttach.lean` for details on the implementation. This only works for higher-order functions that have a suitable setup. See for example section “Well-founded recursion preprocessing setup” in `src/Init/Data/List/Attach.lean`. This does not change the `decreasing_tactic`, so in some cases there is still the need for a manual termination proof some cases. We expect a better termination tactic in the near future.
…prover#6744) This PR extend the preprocessing of well-founded recursive definitions to bring assumptions like `h✝ : x ∈ xs` into scope automatically. This fixes leanprover#5471, and follows (roughly) the design written there. See the module docs at `src/Lean/Elab/PreDefinition/WF/AutoAttach.lean` for details on the implementation. This only works for higher-order functions that have a suitable setup. See for example section “Well-founded recursion preprocessing setup” in `src/Init/Data/List/Attach.lean`. This does not change the `decreasing_tactic`, so in some cases there is still the need for a manual termination proof some cases. We expect a better termination tactic in the near future.
Problem description
Users are often confused and annoyed that code like this does not work:
Instead, one has to write
which
.attach
cruft that gets in the wayGoal
It would be good to address these two issues: Automatically introduce
.attach
etc. where needed, and then hide it again in the equational lemmas (and in the induction principle).Precedence
Something similar is already done by Lean: Any
if … then … else …
written by the user is turned into anif h : … then … else …
, so that the condition is available todecreasing_by
tactic.Solution sketch
My conjecture is that we can achieve something like this by way of rewriting, which allows us to lean on the
simp
machinery (possibly extended by simpprocs where needed, e.g. higher-order matching). This has the benefit of being extensible – clearly lean shouldn’t have hardcoded knowledge about how to thread.attach
’ed information throughList.map
andArray.foldl
.There are three phases
Marking the parameters
We’d define a helper identity function
and when preprocessing the
PreDefinition
, replace all occurrences of a parameterx
withisParam x
.Propagating the information
This is the interesting phase, built on these rewrites:
Propagate through
match
:If we find
replace with
so that we propagate the information about “this is interesting”
(And to achieve confluence with the next rule,
match xs.attach.unattach with …
should behave the same asmatch isParam xs with
.)Introduce
attach
:If we have
and
x
is of an attachable type (List
,Array
,Option
, hopefully extensible), replace it withx.attach.unattach
(whereList.unattach
ismap Subtype.fst
).Propagate
.attach
:An extensible set of rewrite rules propagates
.attach
information, by matching on.unattach
. For example simple things likebut (more interestingly) higher-order rules like
and where if
f x
is a manifest redex, it is reduced (so that the proofh
actually becomes visible).Notice that where possible, the
.unattach
is preserved – maybe the code is a chain of(xs.reverse.filter (…)).map f…
and the recursive call is in the parameter tomap
; we want to propagate the information there.Also notice that we mark the arguments in the higher-order function argument with
isParam
, this way nested Lists are correctly attach-annotated.Removing left-over markers
The last Predefinition preprocessing step is simply
isParam x = x
andxs.attach.unattach = xs
, as the markers have done their job.Cleanup
The three steps above hopefully put the right attach-like information into the context, and will allow Lean to define the function with well-founded recursion. But the user shouldn’t see all that, so we need to clean up. Essentially, this amounts to reversing the rewrite rules above:
The higher-order rewrite rules among these are not good simp lemmas, the simplifier currently does not understand “apply this rule if the argument to
map
does not use the second component”. So eithersimp
needs to be strengthened to understand at such higher-order patterns, or a cusotm simpproc needs to handle these.Using this cleanup stage on the termination proof goals, before proving the equational lemmas and on the type of the functional induction theorem should, at least in most cases, hide all this from the user.
Variations
toParam
marker and rewriting withsimp
, the first three steps could be a simple monolithic pass over theExpr
, keeping track of which variables are “interesting”, and applying the (specially tagged, for extensionality) rewrites itself.Open questions
This adds a yet another fair amount of complexity to the compilation of recursive functions. How reliable will it be? If it only works in simple common cases, but increases the mental load for users who have to do things beyond that, is it worth it?
Maybe it suffices to let the user introduce
.attach
where needed, but clean up the equational lemmas to avoid mentioning it where possible?Or even more simple: If the default
simp
could clean up the usual idioms (by improving it's support for this kind of higher-order patterns), we might improve the user experience without introducing much additional complexity.Community Feedback
–
Impact
Add 👍 to issues you consider important. If others benefit from the changes in this proposal being added, please ask them to add 👍 to it.
The text was updated successfully, but these errors were encountered: