I'm trying to write a Coq function which takes a Stream and a predicate and returns, as a list, the longest prefix of the stream for which the property holds. This is what I have:
Require Import List Streams.
Require Import Lt.
Import ListNotations.
Local Open Scope list_scope.
Section TakeWhile.
Context {A : Type} {P : Stream A -> Prop}.
Hypothesis decide_P : forall s : Stream A, {P s} + {~ P s}.
Program Fixpoint take_while (s : Stream A)
(H : Exists (fun s => ~ P s) s) : list A :=
if decide_P s
then hd s :: take_while (tl s) _
else [].
Next Obligation.
destruct H; tauto.
Defined.
End TakeWhile.
But when I attempt to perform calculations with this function, I get a very large term with all the definitions expanded. I'm not sure why, but I'm guessing that Coq is reluctant to unfold the coinductive Stream argument. Here's an example:
Require Import Program.Equality.
Require Import Compare_dec.
Lemma not_lt_le :
forall n m : nat, ~ n < m -> m <= n.
Proof.
pose le_or_lt.
firstorder.
Qed.
Definition increasing (s : Stream nat) : Prop :=
forall n : nat, Exists (fun s => ~ hd s < n) s.
CoFixpoint nats (n : nat) := Cons n (nats (S n)).
Theorem increasing_nats :
forall n : nat, increasing (nats n).
Proof.
intros n m.
induction m.
- left.
pose lt_n_0.
firstorder.
- dependent induction IHm.
* apply not_lt_le, le_lt_or_eq in H.
destruct H.
+ left.
pose le_not_lt.
firstorder.
+ right.
left.
simpl in *.
rewrite H.
pose lt_irrefl.
firstorder.
* right.
simpl.
apply IHIHm.
reflexivity.
Qed.
Given this, the command Eval compute in take_while (fun s => lt_dec (hd s) 5) (nats 0) (increasing_nats 0 5) leads to a very large term, as I mentioned above.
Can anyone give me some guidance on this?
The problem is that
take_whileis defined recursively onH. Since proofs are often defined opaquely in Coq (as it is the case with yourincreasing_natstheorem),take_whilewon't be able to reduce theincreasing_nats 0 5term and will get stuck, producing the huge term you saw. Even if you ended the proof ofincreasing_natswithDefinedinstead ofQed, forcing its definition to be transparent, that proof uses other lemmas that were also defined opaquely, making evaluation get stuck too.One solution is to prove
increasing_natswithout using any additional lemmas and end the proof withDefined. This approach doesn't scale to more interesting cases, as you may be required to reprove lots of theorems usingDefined.Another solution is to pass
take_whilean explicit bound parameter:If you want to use this function to show that the extracted prefix is maximal, then you would have to show that some element for which
Pdoes not hold occurs insbefore thenth position. Despite this drawback, this function could be more convenient to use, because you don't have to pass a proof object to it.Finally, you could also prove a lemma about
take_whileshowing how it reduces, and apply that lemma every time you want to simplify an expression involving that function. Thus, simplification becomes clumsier, as you need to do it explicitly, but at least you'll be able to prove things abouttake_while.