I'm stuck while trying to define a recursive function in Coq over a particular type of n-ary tree.
The n-ary tree type I'm using is defined as a mutually inductive type as follows:
Inductive tree : Type :=
| leaf : tree
| node : childs -> tree
with childs : Type :=
| childs_one : tree -> childs
| childs_cons : tree -> childs -> childs.
Note that a node has at least one child. If I want to check that a given Boolean property is verified over all the nodes in a tree, I can write a mutually recursive function to go through the whole structure. However, since the childs type is close to the list tree type, and since the standard library offers a lot of lemmas about lists, I want to convert an instance of the childs type into a list and use the fold_left function to do the computation. The function I want to write is as follows :
Fixpoint check_nodes (chk : tree -> bool) (t : tree) {struct t} : bool :=
match t with
| leaf => chk t
| node chs => fold_left (fun b ti => b && check_nodes chk ti) (childs2list chs) true
end.
Here, the childs2list function is a trivial function that converts an instance of the childs type into of list of tree. The check_nodes function does not type-check because the Coq kernel is not able to determine that the ti parameter of the lambda function passed to fold_left is a subterm of t.
I haven't yet managed to find a workaround for this problem. I really want to use the fold_left function for lists in the definition of my function. My attempts to define the function with the Fix combinator, and a well-founded relation (based on a "number of nodes" measure) were not successful.
Does anyone have a solution? Or, must I forget about using the fold_left function?
Here is a complete minimal example:
Require Import Bool.
Require Import List.
Import ListNotations.
Inductive tree : Type :=
| leaf : tree
| node : childs -> tree
with childs : Type :=
| childs_nil : childs
| childs_cons : tree -> childs -> childs.
Fixpoint childs2list (chs : childs) {struct chs} : list tree :=
match chs with
| childs_nil => nil
| childs_cons t chs' => t :: childs2list chs'
end.
Fixpoint check_nodes (chk : tree -> bool) (t : tree) {struct t} : bool :=
match t with
| leaf => chk t
| node chs => fold_left (fun b t__i => b && check_nodes chk t__i) (childs2list chs) true
end.
Many thanks for taking the time to read my question,
Vincent.
I am afraid there isn't a clean way of achieving exactly what you want. One possibility would be to use a nested
listinstead of a mutual inductive, and keep the last child as a separate parameter ofnode. For example:Or, if you want to use
forallbinstead,