I 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 (fx) (map (fun t => mapTree ft) ts) end. Next Obligation.
Unfortunately, at the moment I have proof of commitment height t < height (Node x ts) , not knowing that t is a member of ts .
Similarly to Function instead of Program Fixpoint , only Function detects a problem and cancels the definition:
Error: the term fun t : Tree => mapTree ft can not contain a recursive call to mapTree
I would expect to receive an evidentiary obligation In t ts β height t < height (Node x ts) .
Is there a way to get something that is not related to restructuring a function definition? (I know workarounds that require nesting a map definition here, for example, - Id to avoid this.)
Isabelle
To live up to this expectation, let me show you what happens when I do the same in Isabelle using the Function command, which (AFAIK) is associated with the Coqs 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 (fx) (map (Ξ» t. mapTree ft) 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 proof of the end, I get the assumption t β set ts .
Note that Isabelle does not require a manual completion check here, and the following definition works just fine:
fun mapTree where "mapTree f (Node x ts) = Node (fx) (map (Ξ» t. mapTree ft) ts)"
This works because the map function has a "congruence lemma" of the form
xs = ys βΉ (βx. x β set ys βΉ fx = gx) βΉ map f xs = map g ys
that the Function command uses to find out that proof of completion only needs to be considered t β set ts ..
If such a lemma is not available, for example, because I define
definition "map' = map"
and using this in mapTree , I get the same unforgivable proof as in Coq. I can get it to work again by declaring a congruence lemma for map' , for example. using
declare map_cong[folded map'_def,fundef_cong]