-
Notifications
You must be signed in to change notification settings - Fork 426
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
Structure eta doesn't work for nested inductives #5661
Comments
Properly recursive inductive types and eta for structures don't mix. It makes defeq checking very undecidable, and there are many open research questions about the properties of the resulting type theory. Please no. |
Arthur Adjedj points this out this undecidability problem in #4182 as well. |
Right, but note that I'm not proposing to add recursive structures here; this is for nesting through (itself non-recursive) structures, so the eta cannot go on forever. Or put differently: in the example above analyzing a |
Well, theorem fails (x : StructLike Nested) : Nested.rec_1
(motive_1 := fun _ => Bool) (motive_2 := fun _ => Bool)
(nest := fun _ _ => true)
(other := true)
(mk := fun _ _ => true)
x = true
:=
congrArg (Nested.rec_1
(motive_1 := fun _ => Bool) (motive_2 := fun _ => Bool)
(nest := fun _ _ => true)
(other := true)
(mk := fun _ _ => true)) (show x = ⟨x.1⟩ from rfl) |
Do you mean recursor, do you? Right, that's why it doesn't work at the moment. But could one argue that (One could go one step further and consider defining |
It is neither the constructor nor the projections. The recursor does not participate in the structure eta rule either (which is a relation between the constructor and the primitive projections), although I think there might be special handling to do the same eta expansion I just showed above for stuck structure recursors, and I'm observing that the exact same argument also justifies making I'm mildly negative on #4749, it adds a lot of complexity to the analysis of nested inductives and makes it hard for users and programs to be able to guess what the recursor looks like. |
I think that’s what I am referring to, maybe I shouldn't have called it “structure eta rule” then. My understanding is that the kernel replaces
and I wonder here if that should happen for any recursor that has a major argument of type
Only 80% sure I parse this correctly; does this mean you agree? |
Maybe the fix is actually code simplification: Instead of private def toCtorWhenStructure (inductName : Name) (major : Expr) : MetaM Expr := do
unless (← useEtaStruct inductName) do
return major
let env ← getEnv
if !isStructureLike env inductName then
return major
else if let some _ ← isConstructorApp? major then
return major
else
let majorType ← inferType major
let majorType ← instantiateMVars (← whnf majorType)
let majorTypeI := majorType.getAppFn
if !majorTypeI.isConstOf inductName then
return major
… do not pass private def toCtorWhenStructure (major : Expr) : MetaM Expr := do
if let some _ ← isConstructorApp? major then
return major
let majorType ← inferType major
let majorType ← instantiateMVars (← whnf majorType)
let majorTypeI := majorType.getAppFn
unless majorTypeI.isConst do
return major
let inductName := majorTypeI.constName!
unless (← useEtaStruct inductName) do
return major
let env ← getEnv
if !isStructureLike env inductName then
return major
… (The kernel code is equivalent) |
This is a performance issue. I don't think you want to call |
Thanks, I take it as a “yes” on the overall analysis that these recursors from nested inductives should trigger the structure eta. (It wouldn't be every subterm, but only every major argument of a recursor that didn't reduce to a constructor, and the kernel runs type inference rather often, so I wouldn't jump to conclusions about performance yet. But a flag or a field that stores the IndName of the major argument in the recinfo would of course also work.) |
I dived a bit into the code. The core problem is that
returns “the name of the first inductive name in the mutual group”, but where it’s used it looks like the expectation is that it's “the inductive type of the major argument”. So a less intrusive fix than the one suggested above would be to rename this to
(Incidentially, it seems that one of the few uses of this functions is in |
Previously `RecursorVal.getInduct` would return the prefix of the recursor’s name, which is unlikely the right value for the “derived” recursors in nested recursion. The code using `RecursorVal.getInduct` seems to expect the name of the inductive type of major argument here. If we return that name, this fixes #5661. This bug becomes more visible now that we have structural mutual recursion. (The implementation is still up for discussion; let’s first agree on whether this is the right semantics.) (Also likely worth renaming this function to `getMajorInduct` for clarity.)
Here is a another symptom of this bug: Before:
After #5679:
|
…5679) Previously `RecursorVal.getInduct` would return the prefix of the recursor’s name, which is unlikely the right value for the “derived” recursors in nested recursion. The code using `RecursorVal.getInduct` seems to expect the name of the inductive type of major argument here. If we return that name, this fixes #5661. This bug becomes more visible now that we have structural mutual recursion. Also, to avoid confusion, renames the function to ``getMajorInduct`.
…eanprover#5679) Previously `RecursorVal.getInduct` would return the prefix of the recursor’s name, which is unlikely the right value for the “derived” recursors in nested recursion. The code using `RecursorVal.getInduct` seems to expect the name of the inductive type of major argument here. If we return that name, this fixes leanprover#5661. This bug becomes more visible now that we have structural mutual recursion. Also, to avoid confusion, renames the function to ``getMajorInduct`.
It is unclear if this is really a bug, or should be like this, but I’ll open an issue anyways to record this and ensuing discussion.
Lean’s type theory eta-expands structure like values when reducing, but only when using the type’s “native” recursor, not when reducing derived recursers over that type that are generated as part of nested inductives.
Small reporducer:
One could argue that
Nested.rec_1
should also eta-expand the argument if needed, on the grounds that it’s really just a renamedStructureLike.rec
.Incidentially, #4749 would fix this.
Versions
"4.12.0-nightly-2024-10-09"
Impact
Add 👍 to issues you consider important. If others are impacted by this issue, please ask them to add 👍 to it.
The text was updated successfully, but these errors were encountered: