(** printing elimination %\coqdoctac{elimination}% *) (** printing <= %\rightarrow% #⇐# *) (** * Nested and mutual structurally recursive definitions Example of a term structure with two constructors taking lists of terms. *) From Equations.Prop Require Import Equations. From Stdlib Require Import Program Arith List Compare_dec. Import ListNotations. (** A nested recursive definition of terms with lists of terms *) Inductive term : Set := | Var (n : nat) | Lam (t : term) | App (t : term) (l : list term) | MetaVar (n : nat) (l : list term). (** Defining capture-avoiding substitution for this language: the case of variables. *) Equations subst_var (k : nat) (u : term) (t : nat) : term := subst_var k u n with k ?= n => { | Eq => u; | Gt => Var n; | Lt => Var (pred n) }. (** Nested recursive definition using a top-level [where] definition. The nested recursive fixpoint defined by [subst_tlist] can be used multiple time in [subst_term], and of course recursively call itself and [subst_term]. The regular structural guardedness check is run on this definition to check that it is terminating. Note that one can optionally add a [struct x] annotation to [where] clauses to indicate which arguments decreases explicitely, otherwise _only the last argument_ is tried. *) Equations subst_term (k : nat) (u : term) (t : term) : term := { subst_term k u (Var n) => subst_var k u n; subst_term k u (Lam t) => Lam (subst_term (S k) u t); subst_term k u (App t l) => App (subst_term k u t) (subst_tlist k u l); subst_term k u (MetaVar t l) => MetaVar t (subst_tlist k u l) } where subst_tlist (k : nat) (u : term) (t : list term) : list term := { subst_tlist k u nil => nil; subst_tlist k u (cons t ts) => cons (subst_term k u t) (subst_tlist k u ts) }. (** Remark that our definition of [subst_tlist] is equivalent to a [List.map]: but we need the "expanded" version to properly recognize recursive calls. *) Lemma nested_map k u t : subst_tlist k u t = List.map (subst_term k u) t. Proof. induction t; simpl; rewrite ?IHt; trivial. Qed. (** The elimination principle generated from this definition is giving a conjunction of two predicates as result. One may want to instantiate [P0] with [Forall P] to recover a [map]-like elimination principle. *) Check subst_term_elim : forall (P : nat -> term -> term -> term -> Type) (P0 : nat -> term -> list term -> list term -> Type), (forall (k : nat) (u : term) (n : nat), P k u (Var n) (subst_var k u n)) -> (forall (k : nat) (u t : term), P (S k) u t (subst_term (S k) u t) -> P k u (Lam t) (Lam (subst_term (S k) u t))) -> (forall (k : nat) (u t0 : term) (l : list term), P k u t0 (subst_term k u t0) -> P0 k u l (subst_tlist k u l) -> P k u (App t0 l) (App (subst_term k u t0) (subst_tlist k u l))) -> (forall (k : nat) (u : term) (n0 : nat) (l0 : list term), P0 k u l0 (subst_tlist k u l0) -> P k u (MetaVar n0 l0) (MetaVar n0 (subst_tlist k u l0))) -> (forall (k : nat) (u : term), P0 k u []%list []%list) -> (forall (k : nat) (u t : term) (l : list term), P k u t (subst_term k u t) -> P0 k u l (subst_tlist k u l) -> P0 k u (t :: l)%list (subst_term k u t :: subst_tlist k u l)%list) -> (forall (k : nat) (u t : term), P k u t (subst_term k u t)) * (forall (k : nat) (u : term) (t : list term), P0 k u t (subst_tlist k u t)). (** One can experiment to see that this provides the right induction hypotheses for App and MetaVar *) Lemma subst_subst k u t : subst_term k u t = subst_term k u t. Proof. revert k u t. refine (fst (subst_term_elim (fun k u t c => c = c) (fun k u l c => c = c) _ _ _ _ _ _)); trivial. Qed.