1

I'm having a great difficulty trying to prove even very simple lemmas about a function I defined. This is my definition:

Require Import List.
Require Export Omega.
Require Export FunInd.
Require Export Recdef.

Notation "A :: B" := (cons A B).
Notation "[]" := nil.
Notation "[[ A ]]" := (A :: nil).

Inductive tm :=
| E: nat -> tm
| L: list tm -> tm.

Definition T := list tm.

Fixpoint add_list (l: list nat) : nat :=
  match l with
  | [] => 0
  | n :: l' => n + (add_list l')
  end.

Fixpoint depth (t: tm) : nat :=
  match t with
  | E _ => 1
  | L l => 1 + (add_list (map depth l))
  end.

Definition sum_depth (l: T) := add_list (map depth l).

Function sum_total (l: T) {measure sum_depth l} : nat :=
  match l with
  | [] => 0
  | [[E n]] => n
  | [[L li]] => sum_total li
  | E n :: l' => n + (sum_total l')
  | L li :: l' => (sum_total li) + (sum_total l')
  end.
Proof.
  - auto.
  - intros; unfold sum_depth; subst. simpl; omega.
  - intros; subst; unfold sum_depth; simpl; omega.
  - intros; subst; unfold sum_depth; simpl; omega.
  Defined.

The inductive type can't be changed.

I can prove simple propositions like Lemma test : forall n, sum_total [[E n]] = n. using the compute tactic, but another trivial lemma like Lemma test2 : forall l, sum_total [[L l]] = sum_total l. hangs.

bfbonatto
  • 21
  • 3
  • Instead of writing `Notation "[[ A ]]" := (A :: nil).`, you may want to rely on the notations provided in this module: [`Require Import List. Import ListNotations.`](https://coq.github.io/doc/master/stdlib/Coq.Lists.List.html) – ErikMD Jun 10 '20 at 11:08

2 Answers2

2

First, it seems OK that the compute tactic "hangs" on the goal you mention (because when using the Function … Proof. … Defined. definition methodology, your function sum_total incorporates some proof terms, which are not intended to be computed − all the more on an arbitrary argument l; maybe a tactic such as simpl or cbn would be more suitable in this context).

Independently of my comment on list notations, I had a closer look on your formalization and it seems the Function command is unneeded in your case, because sum_total is essentially structural, so you could use a mere Fixpoint, provided the inductive type you are looking at is slightly rephrased to be defined in one go as a mutually-defined inductive type (see the corresponding doc of the Inductive command in Coq's refman which gives a similar, typical example of "tree / forest").

To elaborate on your example, you may want to adapt your definition (if it is possible for your use case) like this:

Inductive tm :=
| E: nat -> tm
| L: T -> tm

with T :=
  Nil : T
| Cons : forall (e : tm) (l : T), T.

Notation "[[ A ]]" := (Cons A Nil).

Fixpoint sum_total (l: T) {struct l} : nat :=
  match l with
  | Nil => 0
  | [[E n]] => n
  | [[L li]] => sum_total li
  | Cons (E n) l' => n + (sum_total l')
  | Cons (L li) l' => (sum_total li) + (sum_total l')
  end.

(* and the lemma you were talking about is immediate *)
Lemma test2 : forall l, sum_total [[L l]] = sum_total l.
reflexivity.
Qed.

Otherwise (if you cannot rephrase your tm inductive like this), another solution would be to use another strategy than Function to define your sum_total function, e.g. Program Fixpoint, or the Equations plugin (which are much more flexible and robust than Function when dealing with non-structural recursion / dependently-typed pattern matching).

Edit: as the OP mentions the inductive type itself can't be changed, there is a direct solution, even when using the mere Function machinery: relying on the "equation lemma" that is automatically generated by the definition.

To be more precise, if you take your script as is, then you get the following lemma "for free":

Search sum_total "equation".
(*
sum_total_equation:
  forall l : T,
  sum_total l =
  match l with
  | [] => 0
  | [[E n]] => n
  | E n :: (_ :: _) as l' => n + sum_total l'
  | [[L li]] => sum_total li
  | L li :: (_ :: _) as l' => sum_total li + sum_total l'
  end
*)

So you could easily state and prove the lemma you are interested in by doing:

Lemma test2 : forall l, sum_total [[L l]] = sum_total l.
intros l.
rewrite sum_total_equation.
reflexivity.
Qed.
ErikMD
  • 13,377
  • 3
  • 35
  • 71
  • Thank you fort the quick and well-written reply, unfortunately I can't change the inductive type (I'll amend my question to reflect that), meanwhile I'll try a different strategy. – bfbonatto Jun 10 '20 at 17:16
0

Here is an answer that doesn't require changing the inductive type.

There is a simple definition of sum_total that is both comparatively easy to understand and gives (almost) the lemma you are looking for by compute.

Fixpoint sum_tm (t : tm) : nat :=
  match t with
  | E n => n
  | L li => list_sum (map sum_tm li)
  end.
Definition sum_total (l : T) : nat := list_sum (map sum_tm l).

Lemma test2 : forall l, sum_total [[L l]] = sum_total l + 0.
reflexivity.
Qed.

(list_sum comes from the List module.)

Notice how the definition of sum_tm and sum_total exactly follows the structure of the definition of term and T, with list_sum (composed with map) corresponding to the use of list. This pattern is in general effective for these problems with nested inductives.

If you want to get rid of the + 0, you can define a different version of list_sum that includes a case for the singleton list (and you can fuse this with map if you want, though it is not necessary).

That would look like replacing list_sum with list_sum_alt defined as

Fixpoint list_sum_alt (l : list nat) : nat :=
  match l with
  | [] => 0
  | [[n]] => n
  | n :: li => n + list_sum_alt li
  end.

With this definition, test2 holds by compute.

Jasper Hugunin
  • 486
  • 2
  • 8