Zulip Chat Archive

Stream: new members

Topic: Possible to prove termination on Type × Prop?


aron (Jun 07 2025 at 19:05):

Is it possible to prove that a recursive function terminates through structural recursion on both a Type and Prop?

When I try to put termination_by (expr, ha) in the below code it gives me this error:

Application type mismatch: In the application
  (expr, ha)
the argument
  ha
has type
  TypeOf ctx expr a : Prop
but is expected to have type
  ?m.20208 : Type ?u.20205

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
  | pair : (fst : Expr ctx)  (snd : Expr ctx)  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)
  | pair :
    TypeOf ctx fst' fstTy 
    TypeOf ctx snd sndTy 
    TypeOf ctx (.pair fst' snd) (.pair fstTy sndTy)
  | 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



/-- The theorem that every expression has a unique type -/
theorem TypeOf.uniqueness {ctx a b} {expr : Expr ctx} (ha : TypeOf ctx expr a) (hb : TypeOf ctx expr b) : a = b := by
  induction ha
  cases hb; rfl
  cases hb; rfl
  cases hb; rfl
  cases hb; rfl
  cases hb; rfl

  case pair ctx exprFst tyFst exprSnd tySnd typeOfFst typeOfSnd hfst hsnd =>
    generalize b = pairType at *
    cases hb with | pair fstHb sndHb =>
      have fstEq := @TypeOf.uniqueness _ _ _ _ typeOfFst fstHb
      have sndEq := @TypeOf.uniqueness _ _ _ _ typeOfSnd sndHb
      simp
      trivial

  case ife ctx exprCond ifTrueExpr ty ifFalseExpr tyCond tyIfTrue tyIfFalse hCond hIfTrue hIfFalse =>
    generalize b = returnTy at *
    cases hb with | ife condHb trueHb falseHb =>
      have fstEq := @TypeOf.uniqueness _ _ _ ifTrueExpr tyIfTrue trueHb
      -- have sndEq := @TypeOf.uniqueness _ _ _ _ tyIfFalse falseHb
      trivial

termination_by (expr,ha)

aron (Jun 07 2025 at 19:09):

Or is the issue here that Prod.mk only works with Sort 1s and above, i.e. no Props (which have Sort 0)?

Prod.mk.{u, v} {α : Type u} {β : Type v} (fst : α) (snd : β) : α × β

Aaron Liu (Jun 07 2025 at 19:11):

This will be difficult.

aron (Jun 07 2025 at 19:12):

can you elaborate?

Yaël Dillies (Jun 07 2025 at 19:12):

Can you use docs#PProd ?

aron (Jun 07 2025 at 19:14):

Using docs#PProd gives me a different error:

The termination measure's type must not depend on the function's varying parameters, but TypeOf.uniqueness's termination measure does:
  {ctx : Ctx Ty} 
    {a b : Ty}  {expr : Expr ctx}  TypeOf ctx expr a  TypeOf ctx expr b  Expr ctx ×' TypeOf ctx expr a
Try using `sizeOf` explicitly

Aaron Liu (Jun 07 2025 at 19:17):

Try this

/-- The theorem that every expression has a unique type -/
theorem TypeOf.uniqueness {ctx a b} {expr : Expr ctx} (ha : TypeOf ctx expr a) (hb : TypeOf ctx expr b) : a = b := by
  induction ha generalizing b with
  | @pair ctx exprFst tyFst exprSnd tySnd typeOfFst typeOfSnd hfst hsnd =>
    cases hb with | pair fstHb sndHb =>
      have fstEq := hfst fstHb
      have sndEq := hsnd sndHb
      rw [fstEq, sndEq]
  | @ife ctx exprCond ifTrueExpr ty ifFalseExpr tyCond tyIfTrue tyIfFalse hCond hIfTrue hIfFalse =>
    cases hb with | ife condHb trueHb falseHb =>
      exact hIfTrue trueHb
  | _ => cases hb; rfl

aron (Jun 07 2025 at 19:21):

Oh... yeah. How the heck did I miss that :thinking::face_palm: lol

Aaron Liu (Jun 07 2025 at 19:24):

Generally when doing recursion on Prop-valued objects it's best to avoid termination_by since all proofs of the same proposition are equal, and every pair of proofs of any two propositions are HEq.

aron (Jun 07 2025 at 19:29):

Ok so the ife case works perfectly when I put that in my code without touching anything else, but now the pair case doesn't work in my code – presumably because the generalizing does some work here. Although I can't quite wrap my head around what it does exactly

Aaron Liu (Jun 07 2025 at 19:33):

Can you provide a #mwe?

aron (Jun 07 2025 at 19:35):

all I'm saying is that your solution needs the generalizing b in the induction tactic and it breaks if you remove it

aron (Jun 07 2025 at 19:37):

but I don't quite understand what it does, because the only effect it has is inside the cases tactic. Before the cases hb with | pair fstHb sndHb => line b is still just b. It only breaks down into fstTy✝.pair sndTy✝ inside cases hb with | pair fstHb sndHb =>. Which seems odd

Aaron Liu (Jun 07 2025 at 19:39):

generalizing b makes the induction hypothesis universally quantified over all b

aron (Jun 08 2025 at 10:10):

Btw although this has solved my problem what does this error message actually mean? The termination measure's type must not depend on the function's varying parameters, but inferType's termination measure does?

What is a varying parameter and why should termination measures not depend on them? Surely it should depend on a varying parameter, because if it doesn't vary (shrink) how can you know recursion terminates?


Last updated: Dec 20 2025 at 21:32 UTC