(* begin hide *) (**********************************************************************) (* Equations *) (* Copyright (c) 2009-2021 Matthieu Sozeau *) (**********************************************************************) (* This file is distributed under the terms of the *) (* GNU Lesser General Public License Version 2.1 *) (**********************************************************************) From Equations.Prop Require Import Equations. From Stdlib Require Import List Syntax Arith Lia. From Stdlib Require Import List Wellfounded. Import ListNotations. (* end hide *) (** * Mutual well-founded recursion using dependent pattern-matching We present a simple encoding of mutual recursion through the use of dependent pattern-matching on a GADT-like representation of the functions prototypes or just using strong elimination on an enumerated type. We use a simple toy measure here to justify termination, but more elaborate well-founded relations can be used as well. *) Set Equations Transparent. Import Sigma_Notations. (** We first declare the prototypes ouf our mutual definitions. *) Set Universe Polymorphism. Inductive ty : forall (A : Type) (P : A -> Type), Set := | ty0 : ty nat (fun _ => nat) | ty1 : ty (list nat) (fun _ => bool). Derive Signature NoConfusion for ty. (** Our measure is simple, just the natural number or length of the list argument. *) Equations measure : (Σ A P (_ : A), ty A P) -> nat := measure (_, _, a, ty0) => a; measure (_, _, a, ty1) => length a. Definition rel := Program.Wf.MR lt measure. #[local] Instance: WellFounded rel. Proof. red. apply Wf.measure_wf. apply Wf_nat.lt_wf. Defined. Definition pack {A} {P} (x : A) (t : ty A P) := (A, P, x, t) : (Σ A P (_ : A), ty A P). (** We define the function by recursion on the abstract packed argument. Using dependent pattern matching, the clauses for [ty0] refine the argument and return type to [nat] and similarly for [ty1], we can hence do pattern-matching as usual on each separate definition. *) Equations? double_fn {A} {P} (t : ty A P) (x : A) : P x by wf (pack x t) rel := double_fn ty0 n := n + 0; double_fn ty1 nil := true; double_fn ty1 (x :: xs) := 0 true | x :: xs => 0 nat). Equations list_size (l : list A) : nat := list_size nil := 0; list_size (cons x xs) := S (f x + list_size xs). End list_size. Transparent list_size. Section RoseMut. Context {A : Set}. Inductive t : Set := | leaf (a : A) : t | node (l : list t) : t. Derive NoConfusion for t. Equations size (r : t) : nat := size (leaf _) := 0; size (node l) := S (list_size size l). (** An alternative way to define mutual definitions on nested types *) Equations mutmeasure (b : bool) (arg : if b then t else list t) : nat := mutmeasure true t := size t; mutmeasure false lt := list_size size lt. (** The argument and return type depend on the function label ([true] or [false] here) and any well-founded recursive call is allowed. *) Equations? elements (b : bool) (x : if b then t else list t) : if b then list A else list A by wf (mutmeasure b x) lt := elements true (leaf a) := [a]; elements true (node l) := elements false l; elements false nil := nil; elements false (cons t ts) := elements true t ++ elements false ts. Proof. all:lia. Qed. (** Dependent return types are trickier but possible: *) Equations? elements_dep (b : bool) (x : if b then t else list t) : (if b as b' return (if b' then t else list t) -> Set then fun x : t => list A else fun x : list t => list A) x by wf (mutmeasure b x) lt := elements_dep true (leaf a) := [a]; elements_dep true (node l) := elements_dep false l; elements_dep false nil := nil; elements_dep false (cons t ts) := elements_dep true t ++ elements_dep false ts. Proof. all:lia. Qed. End RoseMut.