I’d like to define the following function using Program Fixpoint or Function in Coq:

Require Import Coq.Lists.List. Import ListNotations. Require Import Coq.Program.Wf. Require Import Recdef. Inductive Tree := Node : nat -> list Tree -> Tree. Fixpoint height (t : Tree) : nat := match t with | Node x ts => S (fold_right Nat.max 0 (map height ts)) end. Program Fixpoint mapTree (f : nat -> nat) (t : Tree) {measure (height t)} : Tree := match t with Node x ts => Node (f x) (map (fun t => mapTree f t) ts) end. Next Obligation.

Unfortunately, at this point I have a proof obligation height t < height (Node x ts) without knowing that t is a member of ts .

Similarly with Function instead of Program Fixpoint , only that Function detects the problem and aborts the definition:

Error: the term fun t : Tree => mapTree f t can not contain a recursive call to mapTree

I would expect to get a proof obligation of In t ts → height t < height (Node x ts) .

Is there a way of getting that that does not involve restructuring the function definition? (I know work-arounds that require inlining the definition of map here, for example – I’d like to avoid these.)

Isabelle

To justify that expectation, let me show what happens when I do the same in Isabelle, using the function command, which is (AFAIK) related to Coq’s Function command:

theory Tree imports Main begin datatype Tree = Node nat "Tree list" fun height where "height (Node _ ts) = Suc (foldr max (map height ts) 0)" function mapTree where "mapTree f (Node x ts) = Node (f x) (map (λ t. mapTree f t) ts)" by pat_completeness auto termination proof (relation "measure (λ(f,t). height t)") show "wf (measure (λ(f, t). height t))" by auto next fix f :: "nat ⇒ nat" and x :: nat and ts :: "Tree list" and t assume "t ∈ set ts" thus "((f, t), (f, Node x ts)) ∈ measure (λ(f, t). height t)" by (induction ts) auto qed

In the termination proof, I get the assumption t ∈ set ts .

Note that Isabelle does not require a manual termination proof here, and the following definition works just fine:

fun mapTree where "mapTree f (Node x ts) = Node (f x) (map (λ t. mapTree f t) ts)"

This works because the map function has a “congruence lemma” of the form

xs = ys ⟹ (⋀x. x ∈ set ys ⟹ f x = g x) ⟹ map f xs = map g ys

that the function command uses to find out that the termination proof only needs to consider t ∈ set ts ..

If such a lemma is not available, e.g. because I define

definition "map' = map"

and use that in mapTree , I get the same unprovable proof obligation as in Coq. I can make it work again by declaring a congruence lemma for map' , e.g. using