Zulip Chat Archive
Stream: lean4
Topic: Induction over mutually inductive predicates
Jannis Limperg (Nov 14 2023 at 20:03):
A student of mine is currently struggling with induction over mutually inductive predicates (in Prop
) because
induction
doesn't support mutual inductive types;- mutual recursion is implemented via well-founded recursion, but inductive predicates don't have (and can't have) a useful
SizeOf
instance.
As far as I can tell, he has the following options, none of which are particularly pleasing:
- Recurse over the data rather than the predicates.
- Put the predicates into
Type
. - Use the recursor directly.
- Give up on mutual types and define the union of the predicates as a single inductive predicate, essentially doing the elaborator's job by hand.
Is there a more idiomatic way?
Mario Carneiro (Nov 15 2023 at 07:49):
Nope, this is currently not really supported. The best option for now is just using the recursor directly
Mario Carneiro (Nov 15 2023 at 07:50):
Probably using an inductive family is the path of least resistance
Jannis Limperg (Nov 15 2023 at 10:09):
Okay, thanks!
Max Nowak π (Feb 29 2024 at 13:53):
You can use a mutual block of two definitions to consume the mutually inductive type. Then you wonβt use the induction tactic, but simply obtain the IH by invoking the other definition recursively. It actually works quite nice.
Jannis Limperg (Feb 29 2024 at 14:35):
I think back when I made this thread, your suggestion didn't work for Prop
-valued(!) inductive types because mutual rec went via wfrec, which went via SizeOf
and Prop
-valued types can't have a useful SizeOf
instance. Maybe that has changed in the meantime.
Jonathan Chan (Dec 07 2024 at 21:45):
I noticed that there's structural mutual recursion now: https://github.com/leanprover/lean4/pull/4575
Is there some way of doing mutual induction over inductive predicates with this instead of encoding a mutual predicate as a single inductive family? I've run into an error saying my mutual inductive type
does not have a
.brecOn
recursor
and I'm not sure if there's a trick I'm missing or if indexed predicates are inherently different from propositions
Edward van de Meent (Dec 07 2024 at 22:56):
Afaik using induction on mutual inductives should be possible... Can you post a #mwe?
Jonathan Chan (Dec 10 2024 at 16:16):
okay not so minimal but this is demonstrating the kind of proof I'm trying to do, I'm mechanizing typing judgements and proving syntactic properties:
set_option autoImplicit false
set_option pp.fieldNotation false
inductive Term : Type where
| π° : Nat β Term
open Term
inductive Ctxt : Type where
| nil : Ctxt
| cons : Ctxt β Term β Ctxt
notation:50 "β¬" => Ctxt.nil
infixl:50 "β·" => Ctxt.cons
inductive Eqv : Term β Term β Prop where
| trans {a b c} :
Eqv a b β
Eqv b c β
---------
Eqv a c
| π° {a b} :
a = b β
----------------
Eqv (π° a) (π° b)
infix:40 (priority := 1001) "β" => Eqv
mutual
inductive Wf : Ctxt β Prop where
| nil : Wf (β¬)
| cons {Ξ A k} :
Wf Ξ β
Wt Ξ A (π° k) β
--------------
Wf (Ξ β· A)
inductive Wt : Ctxt β Term β Term β Prop where
| π° {Ξ j k} :
j < k β
-----------------
Wt Ξ (π° j) (π° k)
| conv {Ξ A B a k} :
A β B β
Wt Ξ a A β
Wt Ξ B (π° k) β
--------------
Wt Ξ a B
end
notation:40 "β’" Ξ:40 => Wf Ξ
notation:40 Ξ:41 "β’" a:41 "βΆ" A:41 => Wt Ξ a A
theorem wtπ°Inv {Ξ j A π°'} : Ξ β’ A βΆ π°' β A = π° j β β k, π° k β π°' := by
intro h e; subst e
-- induction h
/- 'induction' tactic does not support mutually inductive types,
the eliminator 'Wt.rec' has multiple motives -/
cases h with
| π° lt => exact β¨_, Eqv.π° rflβ©
| conv eβ h _ =>
let β¨_, eββ© := wtπ°Inv h rfl
exact β¨_, Eqv.trans eβ eββ©
termination_by structural h => h
/- failed to infer structural recursion:
Cannot use parameter h:
unknown constant 'Wt.brecOn' -/
Jannis Limperg (Dec 10 2024 at 16:30):
Induction on mutually defined predicates (in Prop
) still seems to be effectively unsupported. A student of mine is currently struggling with this. The best solution we came up with is to manually define an induction principle for all the mutual types together, using the induction principles for each individual type that Lean generates. But it's a big pain.
Max Nowak π (Dec 10 2024 at 16:41):
I have done mutual theorem
(or mutual def
) before and it works just fine. That is what the induction tactic does under the hood anyway, right? The induction hypothesis is just the result obtained via a recursive call.
Jonathan Chan (Dec 10 2024 at 18:06):
Yeah, my current solution is also to manually define the induction principles I need for the mutual predicate that I've encoded as a single predicate
Jannis Limperg (Dec 10 2024 at 18:10):
Max Nowak π said:
I have done
mutual theorem
(ormutual def
) before and it works just fine. That is what the induction tactic does under the hood anyway, right? The induction hypothesis is just the result obtained via a recursive call.
This does seem to work. I might have to apologise to my student. It seems to do recursion on the mutual types over which the mutual predicates are presumably defined, so no rule induction. But this is probably fine for most use cases.
Jannis Limperg (Dec 10 2024 at 18:10):
A toy example:
mutual
inductive A : Type
| base : A
| good : B β A
| evil : B β A
inductive B : Type
| good : A β B
| evil : A β B
end
mutual
inductive AGood : A β Prop
| base : AGood .base
| good : BGood b β AGood (.good b)
inductive BGood : B β Prop
| good : AGood a β BGood (.good a)
end
mutual
inductive AEvil : A β Prop
| here : AEvil (.evil b)
| there : BEvil b β AEvil (.good b)
inductive BEvil : B β Prop
| here : BEvil (.evil a)
| there : AEvil a β BEvil (.good a)
end
mutual
theorem AGood_not_AEvil : AGood a β Β¬ AEvil a := by
intro h contra
cases h
case base => cases contra
case good p =>
cases contra
apply BGood_not_BEvil <;> assumption
theorem BGood_not_BEvil : BGood b β Β¬ BEvil b := by
intro h contra
cases h
cases contra
case there p => apply AGood_not_AEvil <;> assumption
end
Jannis Limperg (Dec 10 2024 at 18:14):
Thanks @Max Nowak π!
Edward van de Meent (Dec 10 2024 at 20:18):
you can also explicitly invoke .rec
, which i think should work too
Jannis Limperg (Dec 10 2024 at 20:46):
Yes, that also works. But the recursors that Lean generates have a bit of an awkward shape (they ask you to prove one property per mutual type, but then conclude only the property for one of the types). So if you want to go down that route, it's better to first define a more natural recursor/induction principle.
Last updated: May 02 2025 at 03:31 UTC