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. *)