Mercurial > cpdt > repo
comparison src/StackMachine.v @ 15:d8c81e19e7d3
Source language semantics
author | Adam Chlipala <adamc@hcoop.net> |
---|---|
date | Wed, 03 Sep 2008 15:00:56 -0400 |
parents | 8aed4562b62b |
children | 4d8ca6845e67 |
comparison
equal
deleted
inserted
replaced
14:8aed4562b62b | 15:d8c81e19e7d3 |
---|---|
155 (* end hide *) | 155 (* end hide *) |
156 | 156 |
157 (** Though a pencil-and-paper proof might clock out at this point, writing "by a routine induction on [e]," it turns out not to make sense to attack this proof directly. We need to use the standard trick of %\textit{%#<i>#strengthening the induction hypothesis#</i>#%}%. We do that by proving an auxiliary lemma: | 157 (** Though a pencil-and-paper proof might clock out at this point, writing "by a routine induction on [e]," it turns out not to make sense to attack this proof directly. We need to use the standard trick of %\textit{%#<i>#strengthening the induction hypothesis#</i>#%}%. We do that by proving an auxiliary lemma: |
158 *) | 158 *) |
159 | 159 |
160 Lemma compileCorrect' : forall e s p, progDenote (compile e ++ p) s = | 160 Lemma compileCorrect' : forall e p s, progDenote (compile e ++ p) s = progDenote p (expDenote e :: s). |
161 progDenote p (expDenote e :: s). | |
162 | 161 |
163 (** After the period in the [Lemma] command, we are in %\textit{%#<i>#the interactive proof-editing mode#</i>#%}%. We find ourselves staring at this ominous screen of text: | 162 (** After the period in the [Lemma] command, we are in %\textit{%#<i>#the interactive proof-editing mode#</i>#%}%. We find ourselves staring at this ominous screen of text: |
164 | 163 |
165 [[ | 164 [[ |
166 1 subgoal | 165 1 subgoal |
167 | 166 |
168 ============================ | 167 ============================ |
169 forall (e : exp) (s : stack) (p : list instr), | 168 forall (e : exp) (p : list instr) (s : stack), |
170 progDenote (compile e ++ p) s = progDenote p (expDenote e :: s) | 169 progDenote (compile e ++ p) s = progDenote p (expDenote e :: s) |
171 ]] | 170 ]] |
172 | 171 |
173 Coq seems to be restating the lemma for us. What we are seeing is a limited case of a more general protocol for describing where we are in a proof. We are told that we have a single subgoal. In general, during a proof, we can have many pending subgoals, each of which is a logical proposition to prove. Subgoals can be proved in any order, but it usually works best to prove them in the order that Coq chooses. | 172 Coq seems to be restating the lemma for us. What we are seeing is a limited case of a more general protocol for describing where we are in a proof. We are told that we have a single subgoal. In general, during a proof, we can have many pending subgoals, each of which is a logical proposition to prove. Subgoals can be proved in any order, but it usually works best to prove them in the order that Coq chooses. |
174 | 173 |
175 Next in the output, we see our single subgoal described in full detail. There is a double-dashed line, above which would be our free variables and hypotheses, if we had any. Below the line is the conclusion, which, in general, is to be proved from the hypotheses. | 174 Next in the output, we see our single subgoal described in full detail. There is a double-dashed line, above which would be our free variables and hypotheses, if we had any. Below the line is the conclusion, which, in general, is to be proved from the hypotheses. |
473 | 472 |
474 (** In this section, we will build on the initial example by adding additional expression forms that depend on static typing of terms for safety. *) | 473 (** In this section, we will build on the initial example by adding additional expression forms that depend on static typing of terms for safety. *) |
475 | 474 |
476 (** ** Source language *) | 475 (** ** Source language *) |
477 | 476 |
477 (** We define a trivial language of types to classify our expressions: *) | |
478 | |
478 Inductive type : Set := Nat | Bool. | 479 Inductive type : Set := Nat | Bool. |
480 | |
481 (** Now we define an expanded set of binary operators. *) | |
479 | 482 |
480 Inductive tbinop : type -> type -> type -> Set := | 483 Inductive tbinop : type -> type -> type -> Set := |
481 | TPlus : tbinop Nat Nat Nat | 484 | TPlus : tbinop Nat Nat Nat |
482 | TTimes : tbinop Nat Nat Nat | 485 | TTimes : tbinop Nat Nat Nat |
483 | TEq : forall t, tbinop t t Bool | 486 | TEq : forall t, tbinop t t Bool |
484 | TLt : tbinop Nat Nat Bool. | 487 | TLt : tbinop Nat Nat Bool. |
485 | 488 |
489 (** The definition of [tbinop] is different from [binop] in an important way. Where we declared that [binop] has type [Set], here we declare that [tbinop] has type [type -> type -> type -> Set]. We define [tbinop] as an %\textit{%#<i>#indexed type family#</i>#%}%. Indexed inductive types are at the heart of Coq's expressive power; almost everything else of interest is defined in terms of them. | |
490 | |
491 ML and Haskell have indexed algebraic datatypes. For instance, their list types are indexed by the type of data that the list carries. However, compared to Coq, ML and Haskell 98 place two important restrictions on datatype definitions. | |
492 | |
493 First, the indices of the range of each data constructor must be type variables bound at the top level of the datatype definition. There is no way to do what we did here, where we, for instance, say that [TPlus] is a constructor building a [tbinop] whose indices are all fixed at [Nat]. %\textit{%#<i>#Generalized algebraic datatypes (GADTs)#</i>#%}% are a popular feature in GHC Haskell and other languages that removes this first restriction. | |
494 | |
495 The second restriction is not lifted by GADTs. In ML and Haskell, indices of types must be types and may not be %\textit{%#<i>#expressions#</i>#%}%. In Coq, types may be indiced by arbitrary Gallina terms. Type indices can live in the same universe as programs, and we can compute with them just like regular programs. Haskell supports a hobbled form of computation in type indices based on multi-parameter type classes, and recent extensions like type functions bring Haskell programming even closer to "real" functional programming with types, but, without dependent typing, there must always be a gap between how one programs with types and how one programs normally. | |
496 *) | |
497 | |
498 (** We can define a similar type family for typed expressions. *) | |
499 | |
486 Inductive texp : type -> Set := | 500 Inductive texp : type -> Set := |
487 | TNConst : nat -> texp Nat | 501 | TNConst : nat -> texp Nat |
488 | TBConst : bool -> texp Bool | 502 | TBConst : bool -> texp Bool |
489 | TBinop : forall arg1 arg2 res, tbinop arg1 arg2 res -> texp arg1 -> texp arg2 -> texp res. | 503 | TBinop : forall arg1 arg2 res, tbinop arg1 arg2 res -> texp arg1 -> texp arg2 -> texp res. |
490 | 504 |
505 (** Thanks to our use of dependent types, every well-typed [texp] represents a well-typed source expression, by construction. This turns out to be very convenient for many things we might want to do with expressions. For instance, it is easy to adapt our interpreter approach to defining semantics. We start by defining a function mapping the types of our languages into Coq types: *) | |
506 | |
491 Definition typeDenote (t : type) : Set := | 507 Definition typeDenote (t : type) : Set := |
492 match t with | 508 match t with |
493 | Nat => nat | 509 | Nat => nat |
494 | Bool => bool | 510 | Bool => bool |
495 end. | 511 end. |
512 | |
513 (** It can take a few moments to come to terms with the fact that [Set], the type of types of programs, is itself a first-class type, and that we can write functions that return [Set]s. Past that wrinkle, the definition of [typeDenote] is trivial, relying on the [nat] and [bool] types from the Coq standard library. | |
514 | |
515 We need to define a few auxiliary functions, implementing our boolean binary operators that do not appear with the right types in the standard library. They are entirely standard and ML-like, with the one caveat being that the Coq [nat] type uses a unary representation, where [O] is zero and [S n] is the successor of [n]. | |
516 *) | |
496 | 517 |
497 Definition eq_bool (b1 b2 : bool) : bool := | 518 Definition eq_bool (b1 b2 : bool) : bool := |
498 match b1, b2 with | 519 match b1, b2 with |
499 | true, true => true | 520 | true, true => true |
500 | false, false => true | 521 | false, false => true |
512 match n1, n2 with | 533 match n1, n2 with |
513 | O, S _ => true | 534 | O, S _ => true |
514 | S n1', S n2' => lt n1' n2' | 535 | S n1', S n2' => lt n1' n2' |
515 | _, _ => false | 536 | _, _ => false |
516 end. | 537 end. |
538 | |
539 (** Now we can interpret binary operators: *) | |
517 | 540 |
518 Definition tbinopDenote arg1 arg2 res (b : tbinop arg1 arg2 res) | 541 Definition tbinopDenote arg1 arg2 res (b : tbinop arg1 arg2 res) |
519 : typeDenote arg1 -> typeDenote arg2 -> typeDenote res := | 542 : typeDenote arg1 -> typeDenote arg2 -> typeDenote res := |
520 match b in (tbinop arg1 arg2 res) return (typeDenote arg1 -> typeDenote arg2 -> typeDenote res) with | 543 match b in (tbinop arg1 arg2 res) return (typeDenote arg1 -> typeDenote arg2 -> typeDenote res) with |
521 | TPlus => plus | 544 | TPlus => plus |
523 | TEq Nat => eq_nat | 546 | TEq Nat => eq_nat |
524 | TEq Bool => eq_bool | 547 | TEq Bool => eq_bool |
525 | TLt => lt | 548 | TLt => lt |
526 end. | 549 end. |
527 | 550 |
551 (** This function has just a few differences from the denotation functions we saw earlier. First, [tbinop] is an indexed type, so its indices become additional arguments to [tbinopDenote]. Second, we need to perform a genuine %\textit{%#<i>#dependent pattern match#</i>#%}% to come up with a definition of this function that type-checks. In each branch of the [match], we need to use branch-specific information about the indices to [tbinop]. General type inference that takes such information into account is undecidable, and Coq avoids pursuing special heuristics for the problem, instead asking users to write annotations, like we see above on the line with [match]. | |
552 | |
553 The [in] annotation restates the type of the term being case-analyzed. Though we use the same names for the indices as we use in the type of the original argument binder, these are actually fresh variables, and they are %\textit{%#<i>#binding occcurrences#</i>#%}%. Their scope is the [return] clause. That is, [arg1], [arg2], and [arg3] are new bound variables bound only within the return clause [typeDenote arg1 -> typeDenote arg2 -> typeDenote res]. By being explicit about the functional relationship between the type indices and the match result, we regain decidable type inference. | |
554 | |
555 The same tricks suffice to define an expression denotation function in an unsurprising way: | |
556 *) | |
557 | |
528 Fixpoint texpDenote t (e : texp t) {struct e} : typeDenote t := | 558 Fixpoint texpDenote t (e : texp t) {struct e} : typeDenote t := |
529 match e in (texp t) return (typeDenote t) with | 559 match e in (texp t) return (typeDenote t) with |
530 | TNConst n => n | 560 | TNConst n => n |
531 | TBConst b => b | 561 | TBConst b => b |
532 | TBinop _ _ _ b e1 e2 => (tbinopDenote b) (texpDenote e1) (texpDenote e2) | 562 | TBinop _ _ _ b e1 e2 => (tbinopDenote b) (texpDenote e1) (texpDenote e2) |