Ce qui fonctionne
Si vous imbriquez la définition du point fixe sur des listes à l'intérieur de la définition du point fixe sur les arbres, le résultat est bien typé. Il s'agit d'un principe général lorsque vous avez imbriqué la récursivité dans un type inductif, c'est-à-dire lorsque la récursivité passe par un constructeur comme list
.
Fixpoint size (t : LTree) : nat :=
let size_l := (fix size_l (l : list LTree) : nat :=
match l with
| nil => 0
| h::r => size h + size_l r
end) in
match t with Node l =>
1 + size_l l
end.
Ou si vous préférez écrire ceci de façon plus laconique:
Fixpoint size (t : LTree) : nat :=
match t with Node l =>
1 + (fix size_l (l : list LTree) : nat :=
match l with
| nil => 0
| h::r => size h + size_l r
end) l
end.
(Je n'ai aucune idée de qui je l'ai entendu en premier; cela a certainement été découvert plusieurs fois de manière indépendante.)
Un prédicat général de récursivité
Plus généralement, vous pouvez définir LTree
manuellement le principe d'induction «correct» . Le principe d'induction généré automatiquement LTree_rect
omet l'hypothèse de la liste, car le générateur de principe d'induction ne comprend que les occurrences strictement positives non imbriquées de type inductif.
LTree_rect =
fun (P : LTree -> Type) (f : forall l : list LTree, P (Node l)) (l : LTree) =>
match l as l0 return (P l0) with
| Node x => f x
end
: forall P : LTree -> Type,
(forall l : list LTree, P (Node l)) -> forall l : LTree, P l
Ajoutons l'hypothèse d'induction sur les listes. Pour le remplir dans l'appel récursif, nous appelons le principe d'induction de liste et lui passons le principe d'induction d'arbre sur le plus petit arbre à l'intérieur de la liste.
Fixpoint LTree_rect_nest (P : LTree -> Type) (Q : list LTree -> Type)
(f : forall l, Q l -> P (Node l))
(g : Q nil) (h : forall t l, P t -> Q l -> Q (cons t l))
(t : LTree) :=
match t as t0 return (P t0) with
| Node l => f l (list_rect Q g (fun u r => h u r (LTree_rect_nest P Q f g h u)) l)
end.
Pourquoi
La réponse à cette question réside dans les règles précises d'acceptation des fonctions récursives. Ces règles sont forcément subtiles, car il y a un équilibre délicat entre autoriser des cas complexes (comme celui-ci, avec une récursion imbriquée dans le type de données) et des anomalies. Le manuel de référence Coq présente le langage (le calcul des constructions inductives, qui est le langage de preuve de Coq), principalement avec des définitions formellement précises, mais si vous voulez les règles exactes concernant l'induction et la coinduction, vous devrez consulter les articles de recherche, sur ce sujet Eduardo Giménez [1].
Fix
F i x fje{ f1: A1: = t1;F2: A2: = t2}
Γ1Γ2= ( x : L T r e e )= ( l : l i s tL T r e e )UNE1UNE2= n a t= n a tt1t2= c a s e (x, L T r e e ,λy. g1( f2y) )= c a s e (l, l i s tL T r e e ,λhr . g2( f1h ) ( f2r ) )
FjtjeFje
- i = 1j = 2
l
t
size
- i = 2j = 1
h
l
size_l
- i = 2j = 2
r
l
size_l
La raison pour laquelle h
n'est pas structurellement plus petit que l
selon l'interprète Coq n'est pas claire pour moi. D'après ce que je comprends des discussions sur la liste des Coq-clubs [1] [2], il s'agit d'une restriction dans l'interprète, qui pourrait en principe être levée, mais très soigneusement pour éviter d'introduire une incohérence.
Les références
Cocorico, le wiki Coq non terminal: induction mutuelle
Liste de diffusion Coq-Club:
L'équipe de développement Coq. Assistant Coq Proof: Manuel de référence . Version 8.3 (2010). [ web ] ch. 4 .
Eduardo Giménez. Codification des définitions gardées avec des schémas récursifs . Dans Types'94: Types pour les épreuves et les programmes , LNCS 996. Springer-Verlag, 1994. doi: 10.1007 / 3-540-60579-7_3 [ Springer ]
Eduardo Giménez. Définitions structurelles récursives en théorie des types . In ICALP'98: Actes du 25e Colloque international sur les automates, les langues et la programmation. Springer-Verlag, 1998. [ PDF ]