Zulip Chat Archive
Stream: new members
Topic: Possible to not generalize in cases?
aron (Jun 07 2025 at 15:26):
I am using a cases tactic inside of a proof block where I need to show a contradiction. Before the cases I have:
ifTrueTy : Ty
ifTrueProof : TypeOf ctx ifTrue ifTrueTy
ifFalseTy : Ty
ifFalseProof : TypeOf ctx ifFalse ifFalseTy
hbranches : ¬ifTrueTy = ifFalseTy
But inside the ife case I have:
ifTrueTy : Ty
ifTrueProof✝ : TypeOf ctx ifTrue ifTrueTy
ifFalseTy : Ty
ifFalseProof✝ : TypeOf ctx ifFalse ifFalseTy
ifTrueProof : TypeOf ctx ifTrue ty
ifFalseProof : TypeOf ctx ifFalse ty
hbranches : ¬ifTrueTy = ifFalseTy
In other words, the *Proofs get overridden by a version that replaces the last type index with ty. But I can only prove that there is a contradiction if I can show that ifTrueTy = ty and ifFalseTy = ty. But since cases doesn't let me show that ifTrueProof = ifTrueProof✝ I can't do that.
How can I link overridden variables to the new ones that have replaced them?
My #mwe
/-- A list which you add items onto at the end -/
inductive Ctx (α : Type u)
| nil : Ctx α
| add (body : Ctx α) (tip : α) : Ctx α
inductive PrimLitExpr
| unit : PrimLitExpr
| int : Int → PrimLitExpr
| nat : Nat → PrimLitExpr
| bool : Bool → PrimLitExpr
| str : String → PrimLitExpr
inductive Expr : (ctx : Ctx Ty) → Type u where
| primLit : PrimLitExpr → Expr ctx
| ife : (cond : Expr ctx) → (ifTrue : Expr ctx) → (ifFalse : Expr ctx) → Expr ctx
inductive PrimTy
| unit
| int
| nat
| bool
| str
deriving DecidableEq
inductive Ty
| prim : PrimTy → Ty
| pair : (fst : Ty) → (snd : Ty) → Ty
| arrow : (from_ : Ty) → (to_ : Ty) → Ty
deriving DecidableEq
inductive TypeOf : (ctx : Ctx Ty) → (expr : Expr ctx) → (ty : Ty) → Prop where
| primUnit : TypeOf ctx (Expr.primLit .unit) (Ty.prim .unit)
| primInt : TypeOf ctx (Expr.primLit (.int _)) (Ty.prim .int)
| primNat : TypeOf ctx (Expr.primLit (.nat _)) (Ty.prim .nat)
| primBool : TypeOf ctx (Expr.primLit (.bool _)) (Ty.prim .bool)
| primStr : TypeOf ctx (Expr.primLit (.str _)) (Ty.prim .str)
| ife :
TypeOf ctx cond_ (Ty.prim .bool) →
TypeOf ctx ifTrue t →
TypeOf ctx ifFalse t →
TypeOf ctx (Expr.ife cond_ ifTrue ifFalse) t
inductive InferrableType (ctx : Ctx Ty) : (expr : Expr ctx) → Type where
| inferred : (ty : Ty) → (h : TypeOf ctx expr ty) → InferrableType ctx expr
| uninferrable : (h : ∀ ty, ¬TypeOf ctx expr ty) → InferrableType ctx expr
mutual
def inferType (ctx : Ctx Ty) (expr : Expr ctx) : InferrableType ctx expr :=
match h : expr with
| .ife cond ifTrue ifFalse =>
have condDecidable : Decidable _ := TypeOf.decidable ctx cond (.prim .bool)
have ifTrueInferred := inferType ctx ifTrue
have ifFalseInferred := inferType ctx ifFalse
match condDecidable, ifTrueInferred, ifFalseInferred with
| .isTrue condProof, .inferred ifTrueTy ifTrueProof, .inferred ifFalseTy ifFalseProof =>
if hbranches : ifTrueTy = ifFalseTy then
.inferred _ (TypeOf.ife condProof ifTrueProof (hbranches ▸ ifFalseProof))
else
by
refine .uninferrable ?_
intro ty hh
cases hh with
| ife condProof ifTrueProof ifFalseProof =>
-- How can I prove that `ifTrueProof = ifTrueProof✝` and `ifFalseProof = ifFalseProof✝`?
contradiction
def TypeOf.decidable (ctx : Ctx Ty) (expr : Expr ctx) (ty : Ty) : Decidable (TypeOf ctx expr ty) :=
match hexpr : expr, hty : ty with
| .primLit .unit, .prim .unit => .isTrue TypeOf.primUnit
| .primLit (.int _), .prim .int => .isTrue TypeOf.primInt
| .primLit (.nat _), .prim .nat => .isTrue TypeOf.primNat
| .primLit (.bool _), .prim .bool => .isTrue TypeOf.primBool
| .primLit (.str _), .prim .str => .isTrue TypeOf.primStr
end
Eric Wieser (Jun 07 2025 at 16:21):
If you do
| ife condProof ifTrueProof' ifFalseProof' =>
have : ifTrueProof' = ifTrueProof := rfl
you'll see the the statement isn't even well-typed
aron (Jun 07 2025 at 18:13):
hm yeah but uhh... how come? ifTrueTy and ty both have type Ty so I don't understand how they don't have the same type?
aron (Jun 07 2025 at 18:16):
hm ok I don't think that's the real issue because if I do have a hypothesis that ifTrueTy = ty then it type checks just fine
have : ifTrueTy = ty := by sorry
have : ifTrueProof' = (this ▸ ifTrueProof) := by rfl
aron (Jun 07 2025 at 18:33):
Ok I managed to prove the thing but I don't know how it worked. I did:
exact
match hh with
| .ife condProof ifTrueProof ifFalseProof =>
by
have : ty = ifTrueTy := by exact _
rw [← this] at hbranches
have : ty = ifFalseTy := by exact _
rw [← this] at hbranches
contradiction
Full working code
Is there any way to see what terms Lean used to auto-fill holes? Because I'd love to know how it knew that ty = ifTrueTy and ty = ifFalseTy
Eric Wieser (Jun 07 2025 at 18:35):
Lean is not filling the holes at all, it's too busy complaining that your match is incomplete.
Eric Wieser (Jun 07 2025 at 18:35):
You need to add sorry branches to the match to get a useful error
aron (Jun 07 2025 at 18:37):
oh. that's disappointing. alright so i guess my initial question still stands.
aron (Jun 07 2025 at 18:56):
hm actually I think my question was wrong. Because cases is not replacing the original ifTrueTy and ifFalseTy at all... ty comes from the new hypothesis that I am creating by using intro ty hh which is that if ifTrueTy ≠ ifFalseTy then there exists no type ty for which a TypeOf ctx expr ty can be constructed... but I don't think I can prove that with the code I have. I think for that I need to have a theorem that proves that each expression can only have one type, something like
theorem TypeOf.uniqueness {ctx a b} {expr : Expr ctx} (ha : TypeOf ctx expr a) (hb : TypeOf ctx expr b) : a = b
otherwise even though I've shown that ifTrueTy ≠ ifFalseTy, that will only let me prove ¬TypeOf ctx expr ifTrueTy, but not that there is no possible type for expr: ∀ ty, ¬TypeOf ctx expr ty
Last updated: Dec 20 2025 at 21:32 UTC