Zulip Chat Archive
Stream: general
Topic: Understanding shadowing in an inductive family
Markus de Medeiros (May 23 2025 at 12:37):
Hopefully this is a basic question but I've been scratching my head about it. Why is it that in the first case of this inductive type β is shadowed, while in the second case it is not? How would you write an inductive type like this one where the types are never shadowed, ie the arguments to f always have the same types as the arguments to Ty?
inductive Ty : Type _ → Type _ → Type _ where
| C1 : (f : α → β → Prop) → Ty α β
| C2 : (f : α → Prop) → Ty α α
def F {α β : Type _} (t : Ty α β) : Unit :=
match t with
| Ty.C1 f =>
/- α β : Type u_1
t : Ty α β
β : Type u_1 -- β is shadowed
f : α → β → Prop
⊢ Unit -/
sorry
| Ty.C2 f =>
/- α β : Type u_1
t : Ty α β
f : α → Prop
⊢ Unit -/
sorry
Shreyas Srinivas (May 23 2025 at 14:31):
It has something to do with the way \alpha and \beta are implicitly inferred for the constructors
Shreyas Srinivas (May 23 2025 at 14:32):
See below
inductive Ty : Type u → Type u → Type (u + 1) where
| C1 {α β : Type u} : (f : α → β → Prop) → Ty α β
| C2 {α β : Type u}: (f : α → Prop) → Ty α α
def F {α β : Type _} (t : Ty α β) : Unit :=
match t with
| Ty.C1 f => by
/-
α β✝ : Type ?u.710
t : Ty α β✝
β : Type ?u.710
f : α → β → Prop
⊢ Unit -/
sorry
| Ty.C2 f => by
/- α β : Type ?u.710
t : Ty α β
β✝ : Type ?u.710
f : α → Prop-/
sorry
Shreyas Srinivas (May 23 2025 at 14:39):
Then if you change the type of the second constructor to Ty α β the shadowing behaviour disappears
Markus de Medeiros (May 23 2025 at 14:46):
Hm, thanks. This is still quite counterintuitive to me even with α and β made explicit. Unfortunately I do want to avoid the shadowing and I can't change the type of my second constructor, I wonder if there's some way to do this.
Shreyas Srinivas (May 23 2025 at 15:05):
By making the constructors explicit
Shreyas Srinivas (May 23 2025 at 15:05):
inductive Ty : Type _ → Type _ → Type _ where
| C1 : (f : α → β → Prop) → Ty α β
| C2 : (f : α → Prop) → Ty α α
def F {α β : Type _} (t : Ty α β) : Unit :=
match t with
| @Ty.C1 α β f =>by
sorry
| @Ty.C2 α f => by
sorry
Shreyas Srinivas (May 23 2025 at 15:05):
Oh wait that should have worked
Shreyas Srinivas (May 23 2025 at 15:06):
Sorry I take that back
Shreyas Srinivas (May 23 2025 at 15:09):
Okay I have a bad workaround:
inductive Ty : Type _ → Type _ → Type _ where
| C1 {α β} : (f : α → β → Prop) → Ty α β
| C2 {α} : (f : α → Prop) → Ty α α
def F {α β : Type _} (t : @Ty α β) : Unit :=
match h1: t with
| @Ty.C1 α β f =>by
expose_names
let t' := h ▸ t
/-
1 goal
α β✝ : Type ?u.577
t : Ty α β✝
β : Type ?u.577
f : α → β → Prop
h✝ : β✝ = β -- note this
h : HEq t (Ty.C1 f)
⊢ Unit
All Messages (1)
-/
sorry
| @Ty.C2 α f => by
sorry
Shreyas Srinivas (May 23 2025 at 15:12):
I was able to get match to give me the equality between the shadowed and non shadowed \beta
Shreyas Srinivas (May 23 2025 at 15:12):
and then get a t' that I can work with
Shreyas Srinivas (May 23 2025 at 15:13):
You can restrict the scope of tactic mode by putting it inside the let declaration
Shreyas Srinivas (May 23 2025 at 15:13):
inductive Ty : Type _ → Type _ → Type _ where
| C1 {α β} : (f : α → β → Prop) → Ty α β
| C2 {α} : (f : α → Prop) → Ty α α
def F {α β : Type _} (t : @Ty α β) : Unit :=
match h1: t with
| @Ty.C1 α β f =>
let t' := by
expose_names
exact h ▸ t
sorry
| @Ty.C2 α f => sorry
Shreyas Srinivas (May 23 2025 at 15:14):
It's ugly because this substitution stuff doesn't work well with defeq and because I had to use expose_names. I hope someone like Kyle or Mario comes along and saves the day with a better answer.
Shreyas Srinivas (May 23 2025 at 15:17):
The fact that it introduces a second \beta suggests it can't tell whether the \beta in the args of F can be unified with the \beta in the constructor at some step.
Shreyas Srinivas (May 23 2025 at 15:21):
CC : @Mario Carneiro
Mario Carneiro (May 23 2025 at 15:26):
I think the use of indices in Ty is the issue. Lean is able to promote α to a parameter but not β because it changes in C2
Mario Carneiro (May 23 2025 at 15:28):
if C2 instead had an equality argument then they could both be promoted to parameters and you wouldn't have this issue
inductive Ty : Type _ → Type _ → Type _ where
| C1 {α β} : (f : α → β → Prop) → Ty α β
| C2 {α β} : α = β → (f : α → Prop) → Ty α β
def F {α β : Type _} (t : @Ty α β) : Unit := by
match t with
| Ty.C1 f => sorry
| Ty.C2 e f => subst e; sorry
Markus de Medeiros (May 23 2025 at 15:42):
I see. So the variables that can get promoted and hence not shadowed are the ones that are the same in all results of constructors (I don't know what the terminology for this is but the last Ty _ _ in every line).
This is interesting, I think Lean is different to Rocq in this regard no?
Mario Carneiro (May 23 2025 at 15:43):
yes that's right
Mario Carneiro (May 23 2025 at 15:43):
Rocq is different because it uses match+fix instead of recursors
Mario Carneiro (May 23 2025 at 15:43):
the recursor generated for such a type will have to generalize beta
Markus de Medeiros (May 23 2025 at 15:44):
Ty for the explanation!
Shreyas Srinivas (May 23 2025 at 16:09):
Btw, in my “solution” why was the h only included in the context when I added a name for the match?
Mario Carneiro (May 23 2025 at 16:09):
that's what that syntax does
Shreyas Srinivas (May 23 2025 at 16:10):
No that syntax usually only introduces on equality that equates the matched variable to its arms
Mario Carneiro (May 23 2025 at 16:10):
it's not just a name for the match, match _: t is saying to have an equality in scope
Shreyas Srinivas (May 23 2025 at 16:10):
This time it introduced two equalities
Mario Carneiro (May 23 2025 at 16:10):
and you can't have that equality without the dependent equalities for the type in this case
Shreyas Srinivas (May 23 2025 at 16:11):
Oh right okay
Shreyas Srinivas (May 23 2025 at 16:11):
So at the point of the match lean was able to deduce the equality of types you suggested to Markus
Shreyas Srinivas (May 23 2025 at 16:12):
Oh not really. It was a different equality
Mario Carneiro (May 23 2025 at 16:12):
yes, it's also something you can do manually but it's a bit more complicated
Mario Carneiro (May 23 2025 at 16:14):
it's basically the same as:
def F {α β : Type _} (t : @Ty α β) : Unit := by
generalize eq : t = t'; revert t'
match t with
| Ty.C1 f => intro t eq; sorry
| Ty.C2 f => intro t eq; sorry
Mario Carneiro (May 23 2025 at 16:17):
with the dependent form it looks more like:
def F {α β : Type _} (t : @Ty α β) : Unit := by
generalize h : β = β', eq : t = t'; revert eq
match t with
| Ty.C1 f => intro eq; sorry
| Ty.C2 f => intro eq; sorry
Shreyas Srinivas (May 23 2025 at 16:43):
Mario Carneiro said:
with the dependent form it looks more like:
def F {α β : Type _} (t : @Ty α β) : Unit := by generalize h : β = β', eq : t = t'; revert eq match t with | Ty.C1 f => intro eq; sorry | Ty.C2 f => intro eq; sorry
This example seems the same as my last suggestion.
Mario Carneiro (May 23 2025 at 16:46):
yes that's the point, it's showing you what the match eq: t syntax is doing under the hood
Last updated: Dec 20 2025 at 21:32 UTC