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_while
is defined recursively onH
. Since proofs are often defined opaquely in Coq (as it is the case with yourincreasing_nats
theorem),take_while
won't be able to reduce theincreasing_nats 0 5
term and will get stuck, producing the huge term you saw. Even if you ended the proof ofincreasing_nats
withDefined
instead 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_nats
without 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_while
an 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
P
does not hold occurs ins
before then
th 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_while
showing 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
.