Zulip Chat Archive
Stream: Is there code for X?
Topic: How to prove termination when mapping over a list?
Atticus Kuhn (Oct 15 2025 at 17:03):
I'm trying to prove the semantics of a language that has record/product types, so I have the following code:
inductive Ty : Type where
| bool : Ty
| record : (List Ty) → Ty
| string : Ty
| int : Ty
inductive HList {α : Type} (β : α → Type) : List α → Type where
| nil : HList β []
| cons {x xs} : β x → HList β xs → HList β (x :: xs)
@[reducible, simp]
def Ty.denote (t : Ty) : Type :=
match t with
| .bool => Bool
| .int => Int
| .string => String
| .record l => HList Ty.denote l
This gives the error:
termiation.lean:11:2
failed to set reducibility status, `denote` is not a definition
Note: Use `set_option allowUnsafeReducibility true` to override reducibility status validation
termiation.lean:11:13
Cannot add `simp` attribute to `denote`: It is not a proposition nor a definition (to unfold)
Note: The `[simp]` attribute can be added to lemmas that should be automatically used by the simplifier and to definitions that the simplifier should automatically unfold
termiation.lean:12:4
fail to show termination for
Ty.denote
with errors
failed to infer structural recursion:
Cannot use parameter t:
unexpected occurrence of recursive application
denote
failed to prove termination, possible solutions:
- Use `have`-expressions to prove the remaining goals
- Use `termination_by` to specify a different well-founded relation
- Use `decreasing_by` to specify your own tactic for discharging this kind of goal
l : List Ty
t : Ty
⊢ sizeOf t < 1 + sizeOf l
I tried to prove this program is decreasing with:
inductive Ty : Type where
| bool : Ty
| record : (List Ty) → Ty
| string : Ty
| int : Ty
inductive HList {α : Type} (β : α → Type) : List α → Type where
| nil : HList β []
| cons {x xs} : β x → HList β xs → HList β (x :: xs)
@[reducible, simp]
def Ty.denote (t : Ty) : Type :=
match t with
| .bool => Bool
| .int => Int
| .string => String
| .record l => HList Ty.denote l
decreasing_by
simp
sorry
This requires the proof goal
Tactic state
1 goal
l : List Ty
t : Ty
⊢ sizeOf t < 1 + sizeOf l
As far as I understand this, l is a list and t is an element of l. Thus, this should be provable, but how can I ensure that Lean accepts this function?
Robin Arnez (Oct 15 2025 at 17:20):
It isn't very pretty but HList (fun ⟨x, _⟩ => Ty.denote x) l.attach (note that this is a different type though!) The problem with HList Ty.denote l is that Ty.denote is provided in such a way that it might be called with any value, not just the ones in l.
Atticus Kuhn (Oct 16 2025 at 07:00):
Robin Arnez said:
The problem with
HList Ty.denote lis thatTy.denoteis provided in such a way that it might be called with any value, not just the ones inl.
What do you mean by this? I thought that t was an element of l. My intuition is that HList is like a type-level map, so it should map over each element. How could Ty.denote be called on an element not in l?
Atticus Kuhn (Oct 16 2025 at 07:00):
Is there any way to disable the termination checker in Lean? Or is there any downside to marking all definitions in a project as unsafe?
Aaron Liu (Oct 16 2025 at 10:06):
my solution (inspired by docs#List.TProd)
inductive Ty : Type where
| bool : Ty
| record : (List Ty) → Ty
| string : Ty
| int : Ty
-- inductive HList {α : Type} (β : α → Type) : List α → Type where
-- | nil : HList β []
-- | cons {x xs} : β x → HList β xs → HList β (x :: xs)
@[reducible, simp]
def Ty.denote (t : Ty) : Type :=
match t with
| .bool => Bool
| .int => Int
| .string => String
| .record l => Ty.denoteList l
where
@[reducible, simp]
Ty.denoteList (t : List Ty) : Type :=
match t with
| [] => Unit
| x :: xs => Ty.denote x × Ty.denoteList xs
Aaron Liu (Oct 16 2025 at 10:11):
Atticus Kuhn said:
Robin Arnez said:
The problem with
HList Ty.denote lis thatTy.denoteis provided in such a way that it might be called with any value, not just the ones inl.What do you mean by this? I thought that
twas an element ofl. My intuition is thatHListis like a type-level map, so it should map over each element. How couldTy.denotebe called on an element not inl?
well HList could be something weird like
#check fun {α : Type} (β : α → Type) (l : List α) => open Classical in if ∀ x ∉ l, Nonempty (β x) then Nat else l.head?.elim Empty β
which depends on stuff not in l and the termination checker only really sees its type and this has the same type
Last updated: Dec 20 2025 at 21:32 UTC