Mercurial > cpdt > repo
comparison src/Coinductive.v @ 422:a730378789f5
Pass through Coinductive, to incorporate new coqdoc features
author | Adam Chlipala <adam@chlipala.net> |
---|---|
date | Wed, 25 Jul 2012 16:46:53 -0400 |
parents | df7cd24383ae |
children | a54a4a2ea6e4 |
comparison
equal
deleted
inserted
replaced
421:10a6b5414551 | 422:a730378789f5 |
---|---|
10 (* begin hide *) | 10 (* begin hide *) |
11 Require Import List. | 11 Require Import List. |
12 | 12 |
13 Require Import CpdtTactics. | 13 Require Import CpdtTactics. |
14 | 14 |
15 Definition bad := 0. | 15 Definition bad : unit := tt. |
16 | 16 |
17 Set Implicit Arguments. | 17 Set Implicit Arguments. |
18 (* end hide *) | 18 (* end hide *) |
19 | 19 |
20 | 20 |
31 | 31 |
32 This would leave us with [bad tt] as a proof of [P]. | 32 This would leave us with [bad tt] as a proof of [P]. |
33 | 33 |
34 There are also algorithmic considerations that make universal termination very desirable. We have seen how tactics like [reflexivity] compare terms up to equivalence under computational rules. Calls to recursive, pattern-matching functions are simplified automatically, with no need for explicit proof steps. It would be very hard to hold onto that kind of benefit if it became possible to write non-terminating programs; we would be running smack into the halting problem. | 34 There are also algorithmic considerations that make universal termination very desirable. We have seen how tactics like [reflexivity] compare terms up to equivalence under computational rules. Calls to recursive, pattern-matching functions are simplified automatically, with no need for explicit proof steps. It would be very hard to hold onto that kind of benefit if it became possible to write non-terminating programs; we would be running smack into the halting problem. |
35 | 35 |
36 One solution is to use types to contain the possibility of non-termination. For instance, we can create a %``%#"#non-termination monad,#"#%''% inside which we must write all of our general-recursive programs; several such approaches are surveyed in Chapter 7. This is a heavyweight solution, and so we would like to avoid it whenever possible. | 36 One solution is to use types to contain the possibility of non-termination. For instance, we can create a "non-termination monad," inside which we must write all of our general-recursive programs; several such approaches are surveyed in Chapter 7. This is a heavyweight solution, and so we would like to avoid it whenever possible. |
37 | 37 |
38 Luckily, Coq has special support for a class of lazy data structures that happens to contain most examples found in Haskell. That mechanism,%\index{co-inductive types}% _co-inductive types_, is the subject of this chapter. *) | 38 Luckily, Coq has special support for a class of lazy data structures that happens to contain most examples found in Haskell. That mechanism,%\index{co-inductive types}% _co-inductive types_, is the subject of this chapter. *) |
39 | 39 |
40 | 40 |
41 (** * Computing with Infinite Data *) | 41 (** * Computing with Infinite Data *) |
46 Variable A : Type. | 46 Variable A : Type. |
47 | 47 |
48 CoInductive stream : Type := | 48 CoInductive stream : Type := |
49 | Cons : A -> stream -> stream. | 49 | Cons : A -> stream -> stream. |
50 End stream. | 50 End stream. |
51 | |
52 (* begin hide *) | |
53 CoInductive evilStream := Nil. | |
54 (* end hide *) | |
51 | 55 |
52 (** The definition is surprisingly simple. Starting from the definition of [list], we just need to change the keyword [Inductive] to [CoInductive]. We could have left a [Nil] constructor in our definition, but we will leave it out to force all of our streams to be infinite. | 56 (** The definition is surprisingly simple. Starting from the definition of [list], we just need to change the keyword [Inductive] to [CoInductive]. We could have left a [Nil] constructor in our definition, but we will leave it out to force all of our streams to be infinite. |
53 | 57 |
54 How do we write down a stream constant? Obviously simple application of constructors is not good enough, since we could only denote finite objects that way. Rather, whereas recursive definitions were necessary to _use_ values of recursive inductive types effectively, here we find that we need%\index{co-recursive definitions}% _co-recursive definitions_ to _build_ values of co-inductive types effectively. | 58 How do we write down a stream constant? Obviously simple application of constructors is not good enough, since we could only denote finite objects that way. Rather, whereas recursive definitions were necessary to _use_ values of recursive inductive types effectively, here we find that we need%\index{co-recursive definitions}% _co-recursive definitions_ to _build_ values of co-inductive types effectively. |
55 | 59 |
178 (** Coq rejects the following definition that uses [tl]. | 182 (** Coq rejects the following definition that uses [tl]. |
179 [[ | 183 [[ |
180 CoFixpoint bad : stream nat := tl (Cons 0 bad). | 184 CoFixpoint bad : stream nat := tl (Cons 0 bad). |
181 ]] | 185 ]] |
182 | 186 |
183 Imagine that Coq had accepted our definition, and consider how we might evaluate [approx bad 1]. We would be trying to calculate the first element in the stream [bad]. However, it is not hard to see that the definition of [bad] %``%#"#begs the question#"#%''%: unfolding the definition of [tl], we see that we essentially say %``%#"#define [bad] to equal itself#"#%''%! Of course such an equation admits no single well-defined solution, which does not fit well with the determinism of Gallina reduction. | 187 Imagine that Coq had accepted our definition, and consider how we might evaluate [approx bad 1]. We would be trying to calculate the first element in the stream [bad]. However, it is not hard to see that the definition of [bad] "begs the question": unfolding the definition of [tl], we see that we essentially say "define [bad] to equal itself"! Of course such an equation admits no single well-defined solution, which does not fit well with the determinism of Gallina reduction. |
184 | 188 |
185 Since Coq can be considered to check definitions after inlining and simplification of previously defined identifiers, the basic guardedness condition rules out our definition of [bad]. Such an inlining reduces [bad] to: | 189 Since Coq can be considered to check definitions after inlining and simplification of previously defined identifiers, the basic guardedness condition rules out our definition of [bad]. Such an inlining reduces [bad] to: |
186 [[ | 190 [[ |
187 CoFixpoint bad : stream nat := bad. | 191 CoFixpoint bad : stream nat := bad. |
188 ]] | 192 ]] |
207 Definition ones' := map S zeroes. | 211 Definition ones' := map S zeroes. |
208 | 212 |
209 (** The obvious statement of the equality is this: *) | 213 (** The obvious statement of the equality is this: *) |
210 | 214 |
211 Theorem ones_eq : ones = ones'. | 215 Theorem ones_eq : ones = ones'. |
216 | |
217 (* begin hide *) | |
218 Definition foo := @eq. | |
219 (* end hide *) | |
212 | 220 |
213 (** However, faced with the initial subgoal, it is not at all clear how this theorem can be proved. In fact, it is unprovable. The [eq] predicate that we use is fundamentally limited to equalities that can be demonstrated by finite, syntactic arguments. To prove this equivalence, we will need to introduce a new relation. *) | 221 (** However, faced with the initial subgoal, it is not at all clear how this theorem can be proved. In fact, it is unprovable. The [eq] predicate that we use is fundamentally limited to equalities that can be demonstrated by finite, syntactic arguments. To prove this equivalence, we will need to introduce a new relation. *) |
214 (* begin thide *) | 222 (* begin thide *) |
215 | 223 |
216 Abort. | 224 Abort. |
244 ]] | 252 ]] |
245 | 253 |
246 It looks like this proof might be easier than we expected! *) | 254 It looks like this proof might be easier than we expected! *) |
247 | 255 |
248 assumption. | 256 assumption. |
249 (** [[ | 257 (** << |
250 Proof completed. | 258 Proof completed. |
251 ]] | 259 >> |
252 | 260 |
253 Unfortunately, we are due for some disappointment in our victory lap. | 261 Unfortunately, we are due for some disappointment in our victory lap. |
254 [[ | 262 [[ |
255 Qed. | 263 Qed. |
256 ]] | 264 ]] |
331 | Cons h t => Cons (S h) (map t) | 339 | Cons h t => Cons (S h) (map t) |
332 end) zeroes)) | 340 end) zeroes)) |
333 | 341 |
334 ]] | 342 ]] |
335 | 343 |
336 Note that [cofix] notation for anonymous co-recursion, which is analogous to the [fix] notation we have already seen for recursion. Since we have exposed the [Cons] structure of each stream, we can apply the constructor of [stream_eq]. *) | 344 Note the [cofix] notation for anonymous co-recursion, which is analogous to the [fix] notation we have already seen for recursion. Since we have exposed the [Cons] structure of each stream, we can apply the constructor of [stream_eq]. *) |
337 | 345 |
338 constructor. | 346 constructor. |
339 (** [[ | 347 (** [[ |
340 ones_eq : stream_eq ones ones' | 348 ones_eq : stream_eq ones ones' |
341 ============================ | 349 ============================ |
366 Guarded. | 374 Guarded. |
367 ]] | 375 ]] |
368 *) | 376 *) |
369 Abort. | 377 Abort. |
370 | 378 |
371 (** The standard [auto] machinery sees that our goal matches an assumption and so applies that assumption, even though this violates guardedness. One usually starts a proof like this by [destruct]ing some parameter and running a custom tactic to figure out the first proof rule to apply for each case. Alternatively, there are tricks that can be played with %``%#"#hiding#"#%''% the co-inductive hypothesis. | 379 (** The standard [auto] machinery sees that our goal matches an assumption and so applies that assumption, even though this violates guardedness. One usually starts a proof like this by [destruct]ing some parameter and running a custom tactic to figure out the first proof rule to apply for each case. Alternatively, there are tricks that can be played with "hiding" the co-inductive hypothesis. |
372 | 380 |
373 %\medskip% | 381 %\medskip% |
374 | 382 |
375 Must we always be cautious with automation in proofs by co-induction? Induction seems to have dual versions of the same pitfalls inherent in it, and yet we avoid those pitfalls by encapsulating safe Curry-Howard recursion schemes inside named induction principles. It turns out that we can usually do the same with%\index{co-induction principles}% _co-induction principles_. Let us take that tack here, so that we can arrive at an [induction x; crush]-style proof for [ones_eq']. | 383 Must we always be cautious with automation in proofs by co-induction? Induction seems to have dual versions of the same pitfalls inherent in it, and yet we avoid those pitfalls by encapsulating safe Curry-Howard recursion schemes inside named induction principles. It turns out that we can usually do the same with%\index{co-induction principles}% _co-induction principles_. Let us take that tack here, so that we can arrive at an [induction x; crush]-style proof for [ones_eq']. |
376 | 384 |
390 Variable R : stream A -> stream A -> Prop. | 398 Variable R : stream A -> stream A -> Prop. |
391 (** This relation generalizes the theorem we want to prove, characterizing exactly which two arguments to [stream_eq] we want to consider. *) | 399 (** This relation generalizes the theorem we want to prove, characterizing exactly which two arguments to [stream_eq] we want to consider. *) |
392 | 400 |
393 Hypothesis Cons_case_hd : forall s1 s2, R s1 s2 -> hd s1 = hd s2. | 401 Hypothesis Cons_case_hd : forall s1 s2, R s1 s2 -> hd s1 = hd s2. |
394 Hypothesis Cons_case_tl : forall s1 s2, R s1 s2 -> R (tl s1) (tl s2). | 402 Hypothesis Cons_case_tl : forall s1 s2, R s1 s2 -> R (tl s1) (tl s2). |
395 (** Two hypotheses characterize what makes a good choice of [R]: it enforces equality of stream heads, and it is %``%#<i>#hereditary#</i>#%''% in the sense that an [R] stream pair passes on %``%#"#[R]-ness#"#%''% to its tails. An established technical term for such a relation is%\index{bisimulation}% _bisimulation_. *) | 403 (** Two hypotheses characterize what makes a good choice of [R]: it enforces equality of stream heads, and it is %``%#<i>#hereditary#</i>#%''% in the sense that an [R] stream pair passes on "[R]-ness" to its tails. An established technical term for such a relation is%\index{bisimulation}% _bisimulation_. *) |
396 | 404 |
397 (** Now it is straightforward to prove the principle, which says that any stream pair in [R] is equal. The reader may wish to step through the proof script to see what is going on. *) | 405 (** Now it is straightforward to prove the principle, which says that any stream pair in [R] is equal. The reader may wish to step through the proof script to see what is going on. *) |
398 | 406 |
399 Theorem stream_eq_coind : forall s1 s2, R s1 s2 -> stream_eq s1 s2. | 407 Theorem stream_eq_coind : forall s1 s2, R s1 s2 -> stream_eq s1 s2. |
400 cofix; destruct s1; destruct s2; intro. | 408 cofix; destruct s1; destruct s2; intro. |
554 | Const n => n | 562 | Const n => n |
555 | Var v => vs v | 563 | Var v => vs v |
556 | Plus e1 e2 => evalExp vs e1 + evalExp vs e2 | 564 | Plus e1 e2 => evalExp vs e1 + evalExp vs e2 |
557 end. | 565 end. |
558 | 566 |
559 (** Finally, we define a language of commands. It includes variable assignment, sequencing, and a %\texttt{%#<tt>#while#</tt>#%}% form that repeats as long as its test expression evaluates to a nonzero value. *) | 567 (** Finally, we define a language of commands. It includes variable assignment, sequencing, and a <<while>> form that repeats as long as its test expression evaluates to a nonzero value. *) |
560 | 568 |
561 Inductive cmd : Set := | 569 Inductive cmd : Set := |
562 | Assign : var -> exp -> cmd | 570 | Assign : var -> exp -> cmd |
563 | Seq : cmd -> cmd -> cmd | 571 | Seq : cmd -> cmd -> cmd |
564 | While : exp -> cmd -> cmd. | 572 | While : exp -> cmd -> cmd. |
660 <-> evalCmd vs1 c vs2. | 668 <-> evalCmd vs1 c vs2. |
661 intuition; apply optCmd_correct1 || apply optCmd_correct2; assumption. | 669 intuition; apply optCmd_correct1 || apply optCmd_correct2; assumption. |
662 Qed. | 670 Qed. |
663 (* end thide *) | 671 (* end thide *) |
664 | 672 |
665 (** In this form, the theorem tells us that the optimizer preserves observable behavior of both terminating and nonterminating programs, but we did not have to do more work than for the case of terminating programs alone. We merely took the natural inductive definition for terminating executions, made it co-inductive, and applied the appropriate co-induction principle. Curious readers might experiment with adding command constructs like %\texttt{%#<tt>#if#</tt>#%}%; the same proof script should continue working, after the co-induction principle is extended to the new evaluation rules. *) | 673 (** In this form, the theorem tells us that the optimizer preserves observable behavior of both terminating and nonterminating programs, but we did not have to do more work than for the case of terminating programs alone. We merely took the natural inductive definition for terminating executions, made it co-inductive, and applied the appropriate co-induction principle. Curious readers might experiment with adding command constructs like <<if>>; the same proof script should continue working, after the co-induction principle is extended to the new evaluation rules. *) |