comparison src/ProgLang.v @ 398:05efde66559d

Get it working in Coq 8.4beta1; use nice coqdoc notation for italics
author Adam Chlipala <adam@chlipala.net>
date Wed, 06 Jun 2012 11:25:13 -0400
parents d5112c099fbf
children 73f8165a3c1d
comparison
equal deleted inserted replaced
397:d62ed7381a0b 398:05efde66559d
80 (** Now we have some choices as to how we represent the syntax of programs. The two sections of the chapter explore two such choices, demonstrating the effect the choice has on proof complexity. *) 80 (** Now we have some choices as to how we represent the syntax of programs. The two sections of the chapter explore two such choices, demonstrating the effect the choice has on proof complexity. *)
81 81
82 82
83 (** * Dependent de Bruijn Indices *) 83 (** * Dependent de Bruijn Indices *)
84 84
85 (** The first encoding is one we met first in Chapter 9, the %\emph{%#<i>#dependent de Bruijn index#</i>#%}% encoding. We represent program syntax terms in a type familiy parametrized by a list of types, representing the %\emph{%#<i>#typing context#</i>#%}%, or information on which free variables are in scope and what their types are. Variables are represented in a way isomorphic to the natural numbers, where number 0 represents the first element in the context, number 1 the second element, and so on. Actually, instead of numbers, we use the [member] dependent type family from Chapter 9. *) 85 (** The first encoding is one we met first in Chapter 9, the _dependent de Bruijn index_ encoding. We represent program syntax terms in a type familiy parametrized by a list of types, representing the _typing context_, or information on which free variables are in scope and what their types are. Variables are represented in a way isomorphic to the natural numbers, where number 0 represents the first element in the context, number 1 the second element, and so on. Actually, instead of numbers, we use the [member] dependent type family from Chapter 9. *)
86 86
87 Module FirstOrder. 87 Module FirstOrder.
88 88
89 (** Here is the definition of the [term] type, including variables, constants, addition, function abstraction and application, and let binding of local variables. *) 89 (** Here is the definition of the [term] type, including variables, constants, addition, function abstraction and application, and let binding of local variables. *)
90 90
122 | App _ _ _ e1 e2 => fun s => (termDenote e1 s) (termDenote e2 s) 122 | App _ _ _ e1 e2 => fun s => (termDenote e1 s) (termDenote e2 s)
123 123
124 | Let _ _ _ e1 e2 => fun s => termDenote e2 (termDenote e1 s ::: s) 124 | Let _ _ _ e1 e2 => fun s => termDenote e2 (termDenote e1 s ::: s)
125 end. 125 end.
126 126
127 (** With this term representation, some program transformations are easy to implement and prove correct. Certainly we would be worried if this were not the the case for the %\emph{%#<i>#identity#</i>#%}% transformation, which takes a term apart and reassembles it. *) 127 (** With this term representation, some program transformations are easy to implement and prove correct. Certainly we would be worried if this were not the the case for the _identity_ transformation, which takes a term apart and reassembles it. *)
128 128
129 Fixpoint ident G t (e : term G t) : term G t := 129 Fixpoint ident G t (e : term G t) : term G t :=
130 match e with 130 match e with
131 | Var _ _ x => Var x 131 | Var _ _ x => Var x
132 132
142 Theorem identSound : forall G t (e : term G t) s, 142 Theorem identSound : forall G t (e : term G t) s,
143 termDenote (ident e) s = termDenote e s. 143 termDenote (ident e) s = termDenote e s.
144 induction e; t. 144 induction e; t.
145 Qed. 145 Qed.
146 146
147 (** A slightly more ambitious transformation belongs to the family of %\emph{%#<i>#constant folding#</i>#%}% optimizations we have used as examples in other chapters. *) 147 (** A slightly more ambitious transformation belongs to the family of _constant folding_ optimizations we have used as examples in other chapters. *)
148
149 Axiom admit : forall T, T.
148 150
149 Fixpoint cfold G t (e : term G t) : term G t := 151 Fixpoint cfold G t (e : term G t) : term G t :=
150 match e with 152 match e with
151 | Plus G e1 e2 => 153 | Plus G e1 e2 =>
152 let e1' := cfold e1 in 154 let e1' := cfold e1 in
153 let e2' := cfold e2 in 155 let e2' := cfold e2 in
154 match e1', e2' return _ with 156 let maybeOpt := match e1' return _ with
155 | Const _ n1, Const _ n2 => Const (n1 + n2) 157 | Const _ n1 =>
156 | _, _ => Plus e1' e2' 158 match e2' return _ with
157 end 159 | Const _ n2 => Some (Const (n1 + n2))
160 | _ => None
161 end
162 | _ => None
163 end in
164 match maybeOpt with
165 | None => Plus e1' e2'
166 | Some e' => e'
167 end
158 168
159 | Abs _ _ _ e1 => Abs (cfold e1) 169 | Abs _ _ _ e1 => Abs (cfold e1)
160 | App _ _ _ e1 e2 => App (cfold e1) (cfold e2) 170 | App _ _ _ e1 e2 => App (cfold e1) (cfold e2)
161 171
162 | Let _ _ _ e1 e2 => Let (cfold e1) (cfold e2) 172 | Let _ _ _ e1 e2 => Let (cfold e1) (cfold e2)
176 | Let _ _ _ _ _ => _ 186 | Let _ _ _ _ _ => _
177 end] ] => dep_destruct E 187 end] ] => dep_destruct E
178 end; t). 188 end; t).
179 Qed. 189 Qed.
180 190
181 (** The transformations we have tried so far have been straightforward because they do not have interesting effects on the variable binding structure of terms. The dependent de Bruijn representation is called %\index{first-order syntax}\emph{%#<i>#first-order#</i>#%}% because it encodes variable identity explicitly; all such representations incur bookkeeping overheads in transformations that rearrange binding structure. 191 (** The transformations we have tried so far have been straightforward because they do not have interesting effects on the variable binding structure of terms. The dependent de Bruijn representation is called %\index{first-order syntax}%_first-order_ because it encodes variable identity explicitly; all such representations incur bookkeeping overheads in transformations that rearrange binding structure.
182 192
183 As an example of a tricky transformation, consider one that removes all uses of %``%#"#[let x = e1 in e2]#"#%''% by substituting [e1] for [x] in [e2]. We will implement the translation by pairing the %``%#"#compile-time#"#%''% typing environment with a %``%#"#run-time#"#%''% value environment or %\emph{%#<i>#substitution#</i>#%}%, mapping each variable to a value to be substituted for it. Such a substitute term may be placed within a program in a position with a larger typing environment than applied at the point where the substitute term was chosen. To support such context transplantation, we need %\emph{%#<i>#lifting#</i>#%}%, a standard de Bruijn indices operation. With dependent typing, lifting corresponds to weakening for typing judgments. 193 As an example of a tricky transformation, consider one that removes all uses of %``%#"#[let x = e1 in e2]#"#%''% by substituting [e1] for [x] in [e2]. We will implement the translation by pairing the %``%#"#compile-time#"#%''% typing environment with a %``%#"#run-time#"#%''% value environment or _substitution_, mapping each variable to a value to be substituted for it. Such a substitute term may be placed within a program in a position with a larger typing environment than applied at the point where the substitute term was chosen. To support such context transplantation, we need _lifting_, a standard de Bruijn indices operation. With dependent typing, lifting corresponds to weakening for typing judgments.
184 194
185 The fundamental goal of lifting is to add a new variable to a typing context, maintaining the validity of a term in the expanded context. To express the operation of adding a type to a context, we use a helper function [insertAt]. *) 195 The fundamental goal of lifting is to add a new variable to a typing context, maintaining the validity of a term in the expanded context. To express the operation of adding a type to a context, we use a helper function [insertAt]. *)
186 196
187 Fixpoint insertAt (t : type) (G : list type) (n : nat) {struct n} : list type := 197 Fixpoint insertAt (t : type) (G : list type) (n : nat) {struct n} : list type :=
188 match n with 198 match n with
220 | App _ _ _ e1 e2 => App (lift' t' n e1) (lift' t' n e2) 230 | App _ _ _ e1 e2 => App (lift' t' n e1) (lift' t' n e2)
221 231
222 | Let _ _ _ e1 e2 => Let (lift' t' n e1) (lift' t' (S n) e2) 232 | Let _ _ _ e1 e2 => Let (lift' t' n e1) (lift' t' (S n) e2)
223 end. 233 end.
224 234
225 (** In the [Let] removal transformation, we only need to apply lifting to add a new variable at the %\emph{%#<i>#beginning#</i>#%}% of a typing context, so we package lifting into this final, simplified form. *) 235 (** In the [Let] removal transformation, we only need to apply lifting to add a new variable at the _beginning_ of a typing context, so we package lifting into this final, simplified form. *)
226 236
227 Definition lift G t' t (e : term G t) : term (t' :: G) t := 237 Definition lift G t' t (e : term G t) : term (t' :: G) t :=
228 lift' t' O e. 238 lift' t' O e.
229 239
230 (** Finally, we can implement [Let] removal. The argument of type [hlist (term G') G] represents a substitution mapping each variable from context [G] into a term that is valid in context [G']. Note how the [Abs] case (1) extends via lifting the substitution [s] to hold in the broader context of the abstraction body [e1] and (2) maps the new first variable to itself. It is only the [Let] case that maps a variable to any substitute beside itself. *) 240 (** Finally, we can implement [Let] removal. The argument of type [hlist (term G') G] represents a substitution mapping each variable from context [G] into a term that is valid in context [G']. Note how the [Abs] case (1) extends via lifting the substitution [s] to hold in the broader context of the abstraction body [e1] and (2) maps the new first variable to itself. It is only the [Let] case that maps a variable to any substitute beside itself. *)
306 (** The [Let] removal optimization is a good case study of a simple transformation that may turn out to be much more work than expected, based on representation choices. In the second part of this chapter, we consider an alternate choice that produces a more pleasant experience. *) 316 (** The [Let] removal optimization is a good case study of a simple transformation that may turn out to be much more work than expected, based on representation choices. In the second part of this chapter, we consider an alternate choice that produces a more pleasant experience. *)
307 317
308 318
309 (** * Parametric Higher-Order Abstract Syntax *) 319 (** * Parametric Higher-Order Abstract Syntax *)
310 320
311 (** In contrast to first-order encodings, %\index{higher-order syntax}\emph{%#<i>#higher-order#</i>#%}% encodings avoid explicit modeling of variable identity. Instead, the binding constructs of an %\index{object language}\emph{%#<i>#object language#</i>#%}% (the language being formalized) can be represented using the binding constructs of the %\index{meta language}\emph{%#<i>#meta language#</i>#%}% (the language in which the formalization is done). The best known higher-order encoding is called %\index{higher-order abstract syntax}\index{HOAS}\emph{%#<i>#higher-order abstract syntax (HOAS)#</i>#%}~\cite{HOAS}%, and we can start by attempting to apply it directly in Coq. *) 321 (** In contrast to first-order encodings, %\index{higher-order syntax}%_higher-order_ encodings avoid explicit modeling of variable identity. Instead, the binding constructs of an %\index{object language}%_object language_ (the language being formalized) can be represented using the binding constructs of the %\index{meta language}%_meta language_ (the language in which the formalization is done). The best known higher-order encoding is called %\index{higher-order abstract syntax}\index{HOAS}%_higher-order abstract syntax (HOAS)_ %\cite{HOAS}%, and we can start by attempting to apply it directly in Coq. *)
312 322
313 Module HigherOrder. 323 Module HigherOrder.
314 324
315 (** With HOAS, each object language binding construct is represented with a %\emph{%#<i>#function#</i>#%}% of the meta language. Here is what we get if we apply that idea within an inductive definition of term syntax. *) 325 (** With HOAS, each object language binding construct is represented with a _function_ of the meta language. Here is what we get if we apply that idea within an inductive definition of term syntax. *)
316 326
317 (** %\vspace{-.15in}%[[ 327 (** %\vspace{-.15in}%[[
318 Inductive term : type -> Type := 328 Inductive term : type -> Type :=
319 | Const : nat -> term Nat 329 | Const : nat -> term Nat
320 | Plus : term Nat -> term Nat -> term Nat 330 | Plus : term Nat -> term Nat -> term Nat
325 | Let : forall t1 t2, term t1 -> (term t1 -> term t2) -> term t2. 335 | Let : forall t1 t2, term t1 -> (term t1 -> term t2) -> term t2.
326 ]] 336 ]]
327 337
328 However, Coq rejects this definition for failing to meet the %\index{strict positivity restriction}%strict positivity restriction. For instance, the constructor [Abs] takes an argument that is a function over the same type family [term] that we are defining. Inductive definitions of this kind can be used to write non-terminating Gallina programs, which breaks the consistency of Coq's logic. 338 However, Coq rejects this definition for failing to meet the %\index{strict positivity restriction}%strict positivity restriction. For instance, the constructor [Abs] takes an argument that is a function over the same type family [term] that we are defining. Inductive definitions of this kind can be used to write non-terminating Gallina programs, which breaks the consistency of Coq's logic.
329 339
330 An alternate higher-order encoding is %\index{parametric higher-order abstract syntax}\index{PHOAS}\emph{%#<i>#parametric HOAS#</i>#%}%, as introduced by Washburn and Weirich%~\cite{BGB}% for Haskell and tweaked by me%~\cite{PhoasICFP08}% for use in Coq. Here the idea is to parametrize the syntax type by a type family standing for a %\emph{%#<i>#representation of variables#</i>#%}%. *) 340 An alternate higher-order encoding is %\index{parametric higher-order abstract syntax}\index{PHOAS}%_parametric HOAS_, as introduced by Washburn and Weirich%~\cite{BGB}% for Haskell and tweaked by me%~\cite{PhoasICFP08}% for use in Coq. Here the idea is to parametrize the syntax type by a type family standing for a _representation of variables_. *)
331 341
332 Section var. 342 Section var.
333 Variable var : type -> Type. 343 Variable var : type -> Type.
334 344
335 Inductive term : type -> Type := 345 Inductive term : type -> Type :=
346 356
347 Implicit Arguments Var [var t]. 357 Implicit Arguments Var [var t].
348 Implicit Arguments Const [var]. 358 Implicit Arguments Const [var].
349 Implicit Arguments Abs [var dom ran]. 359 Implicit Arguments Abs [var dom ran].
350 360
351 (** Coq accepts this definition because our embedded functions now merely take %\emph{%#<i>#variables#</i>#%}% as arguments, instead of arbitrary terms. One might wonder whether there is an easy loophole to exploit here, instantiating the parameter [var] as [term] itself. However, to do that, we would need to choose a variable representation for this nested mention of [term], and so on through an infinite descent into [term] arguments. 361 (** Coq accepts this definition because our embedded functions now merely take _variables_ as arguments, instead of arbitrary terms. One might wonder whether there is an easy loophole to exploit here, instantiating the parameter [var] as [term] itself. However, to do that, we would need to choose a variable representation for this nested mention of [term], and so on through an infinite descent into [term] arguments.
352 362
353 We write the final type of a closed term using polymorphic quantification over all possible choices of [var] type family. *) 363 We write the final type of a closed term using polymorphic quantification over all possible choices of [var] type family. *)
354 364
355 Definition Term t := forall var, term var t. 365 Definition Term t := forall var, term var t.
356 366
357 (** Here are the new representations of the example terms from the last section. Note how each is written as a function over a [var] choice, such that the specific choice has no impact on the %\emph{%#<i>#structure#</i>#%}% of the term. *) 367 (** Here are the new representations of the example terms from the last section. Note how each is written as a function over a [var] choice, such that the specific choice has no impact on the _structure_ of the term. *)
358 368
359 Example add : Term (Func Nat (Func Nat Nat)) := fun var => 369 Example add : Term (Func Nat (Func Nat Nat)) := fun var =>
360 Abs (fun x => Abs (fun y => Plus (Var x) (Var y))). 370 Abs (fun x => Abs (fun y => Plus (Var x) (Var y))).
361 371
362 Example three_the_hard_way : Term Nat := fun var => 372 Example three_the_hard_way : Term Nat := fun var =>
363 App (App (add var) (Const 1)) (Const 2). 373 App (App (add var) (Const 1)) (Const 2).
364 374
365 (** The argument [var] does not even appear in the function body for [add]. How can that be? By giving our terms expressive types, we allow Coq to infer many arguments for us. In fact, we do not even need to name the [var] argument! Even though these formal parameters appear as underscores, they %\emph{%#<i>#are#</i>#%}% mentioned in the function bodies that type inference calculates. *) 375 (** The argument [var] does not even appear in the function body for [add]. How can that be? By giving our terms expressive types, we allow Coq to infer many arguments for us. In fact, we do not even need to name the [var] argument! Even though these formal parameters appear as underscores, they _are_ mentioned in the function bodies that type inference calculates. *)
366 376
367 Example add' : Term (Func Nat (Func Nat Nat)) := fun _ => 377 Example add' : Term (Func Nat (Func Nat Nat)) := fun _ =>
368 Abs (fun x => Abs (fun y => Plus (Var x) (Var y))). 378 Abs (fun x => Abs (fun y => Plus (Var x) (Var y))).
369 379
370 Example three_the_hard_way' : Term Nat := fun _ => 380 Example three_the_hard_way' : Term Nat := fun _ =>
371 App (App (add' _) (Const 1)) (Const 2). 381 App (App (add' _) (Const 1)) (Const 2).
372 382
373 383
374 (** ** Functional Programming with PHOAS *) 384 (** ** Functional Programming with PHOAS *)
375 385
376 (** It may not be at all obvious that the PHOAS representation admits the crucial computable operations. The key to effective deconstruction of PHOAS terms is one principle: treat the [var] parameter as an unconstrained choice of %\emph{%#<i>#which data should be annotated on each variable#</i>#%}%. We will begin with a simple example, that of counting how many variable nodes appear in a PHOAS term. This operation requires no data annotated on variables, so we simply annotate variables with [unit] values. Note that, when we go under binders in the cases for [Abs] and [Let], we must provide the data value to annotate on the new variable we pass beneath. For our current choice of [unit] data, we always pass [tt]. *) 386 (** It may not be at all obvious that the PHOAS representation admits the crucial computable operations. The key to effective deconstruction of PHOAS terms is one principle: treat the [var] parameter as an unconstrained choice of _which data should be annotated on each variable_. We will begin with a simple example, that of counting how many variable nodes appear in a PHOAS term. This operation requires no data annotated on variables, so we simply annotate variables with [unit] values. Note that, when we go under binders in the cases for [Abs] and [Let], we must provide the data value to annotate on the new variable we pass beneath. For our current choice of [unit] data, we always pass [tt]. *)
377 387
378 Fixpoint countVars t (e : term (fun _ => unit) t) : nat := 388 Fixpoint countVars t (e : term (fun _ => unit) t) : nat :=
379 match e with 389 match e with
380 | Var _ _ => 1 390 | Var _ _ => 1
381 391
425 (** %\vspace{-.15in}%[[ 435 (** %\vspace{-.15in}%[[
426 = "(((fun x => (fun x' => (x + x'))) N) N)" 436 = "(((fun x => (fun x' => (x + x'))) N) N)"
427 ]] 437 ]]
428 *) 438 *)
429 439
430 (** However, it is not necessary to convert to first-order form to support many common operations on terms. For instance, we can implement substitution of one term in another. The key insight here is to %\emph{%#<i>#tag variables with terms#</i>#%}%, so that, on encountering a variable, we can simply replace it by the term in its tag. We will call this function initially on a term with exactly one free variable, tagged with the appropriate substitute. During recursion, new variables are added, but they are only tagged with their own term equivalents. Note that this function [squash] is parametrized over a specific [var] choice. *) 440 (** However, it is not necessary to convert to first-order form to support many common operations on terms. For instance, we can implement substitution of one term in another. The key insight here is to _tag variables with terms_, so that, on encountering a variable, we can simply replace it by the term in its tag. We will call this function initially on a term with exactly one free variable, tagged with the appropriate substitute. During recursion, new variables are added, but they are only tagged with their own term equivalents. Note that this function [squash] is parametrized over a specific [var] choice. *)
431 441
432 Fixpoint squash var t (e : term (term var) t) : term var t := 442 Fixpoint squash var t (e : term (term var) t) : term var t :=
433 match e with 443 match e with
434 | Var _ e1 => e1 444 | Var _ e1 => e1
435 445
461 (fun x : var Nat => 471 (fun x : var Nat =>
462 Abs (fun y : var Nat => Plus (Var x) (Var y)))) 472 Abs (fun y : var Nat => Plus (Var x) (Var y))))
463 (Const 1)) (Const 2)) (Const 3) 473 (Const 1)) (Const 2)) (Const 3)
464 ]] 474 ]]
465 475
466 One further development, which may seem surprising at first, is that we can also implement a usual term denotation function, when we %\emph{%#<i>#tag variables with their denotations#</i>#%}%. *) 476 One further development, which may seem surprising at first, is that we can also implement a usual term denotation function, when we _tag variables with their denotations_. *)
467 477
468 Fixpoint termDenote t (e : term typeDenote t) : typeDenote t := 478 Fixpoint termDenote t (e : term typeDenote t) : typeDenote t :=
469 match e with 479 match e with
470 | Var _ v => v 480 | Var _ v => v
471 481
692 Have we really avoided first-order reasoning about the output terms of translations? The answer depends on some subtle issues, which deserve a subsection of their own. *) 702 Have we really avoided first-order reasoning about the output terms of translations? The answer depends on some subtle issues, which deserve a subsection of their own. *)
693 703
694 704
695 (** ** Establishing Term Well-Formedness *) 705 (** ** Establishing Term Well-Formedness *)
696 706
697 (** Can there be values of type [Term t] that are not well-formed according to [Wf]? We expect that Gallina satisfies key %\index{parametricity}\emph{%#<i>#parametricity#</i>#%}~\cite{parametricity}% properties, which indicate how polymorphic types may only be inhabited by specific values. We omit details of parametricity theorems here, but [forall t (E : Term t), Wf E] follows the flavor of such theorems. One option would be to assert that fact as an axiom, %``%#"#proving#"#%''% that any output of any of our translations is well-formed. We could even prove the soundness of the theorem on paper meta-theoretically, say by considering some particular model of CIC. 707 (** Can there be values of type [Term t] that are not well-formed according to [Wf]? We expect that Gallina satisfies key %\index{parametricity}%_parametricity_ %\cite{parametricity}% properties, which indicate how polymorphic types may only be inhabited by specific values. We omit details of parametricity theorems here, but [forall t (E : Term t), Wf E] follows the flavor of such theorems. One option would be to assert that fact as an axiom, %``%#"#proving#"#%''% that any output of any of our translations is well-formed. We could even prove the soundness of the theorem on paper meta-theoretically, say by considering some particular model of CIC.
698 708
699 To be more cautious, we could prove [Wf] for every term that interests us, threading such proofs through all transformations. Here is an example exercise of that kind, for [Unlet]. 709 To be more cautious, we could prove [Wf] for every term that interests us, threading such proofs through all transformations. Here is an example exercise of that kind, for [Unlet].
700 710
701 First, we prove that [wf] is %\emph{%#<i>#monotone#</i>#%}%, in that a given instance continues to hold as we add new variable pairs to the variable isomorphism. *) 711 First, we prove that [wf] is _monotone_, in that a given instance continues to hold as we add new variable pairs to the variable isomorphism. *)
702 712
703 Hint Constructors wf. 713 Hint Constructors wf.
704 Hint Extern 1 (In _ _) => simpl; tauto. 714 Hint Extern 1 (In _ _) => simpl; tauto.
705 Hint Extern 1 (Forall _ _) => eapply Forall_weaken; [ eassumption | simpl ]. 715 Hint Extern 1 (Forall _ _) => eapply Forall_weaken; [ eassumption | simpl ].
706 716