a verified staged interpreter is a verified compiler

Upload: edwin-brady

Post on 30-May-2018

247 views

Category:

Documents


0 download

TRANSCRIPT

  • 8/14/2019 A Verified Staged Interpreter is a Verified Compiler

    1/10

    A Verified Staged Interpreter is a Verified Compiler

    Multi-stage Programming with Dependent Types

    Edwin Brady Kevin Hammond

    School of Computer Science, University of St Andrews, St Andrews, Scotland.

    E m a i l : e b , k h @ d c s . s t - a n d . a c . u k

    Abstract

    Dependent types and multi-stage programming have both beenused, separately, in programming language design and implemen-tation. Each technique has its own advantages with dependenttypes, we can verify aspects of interpreters and compilers such astype safety and stack invariants. Multi-stage programming, on theother hand, can give the implementor access to underlying com-

    piler technology; a staged interpreter is a translator. In this paper,we investigate the combination of these techniques. We implementan interpreter for a simply typed lambda calculus, using dependenttypes to guarantee correctness properties by construction. We giveexplicit proofs of these correctness properties, then add staging an-notations to generate a translator from the interpreter. In this way,we have constructed a verified compiler from a verified staged in-terpreter. We illustrate the application of the technique by consid-ering a simple staged interpreter that provides guarantees for somesimple resource bound properties, as might be found in a domainspecific language for real-time embedded systems.

    Categories and Subject Descriptors D.3.4 [Programming Lan-guages]: Processors Interpreters, Compilers, Translator writ-ing systems and compiler generators; D.2.4 [Software Engineer-ing]: Software/Program Verification Correctness proofs, Formalmethods

    General Terms Languages, Theory, Verification

    Keywords Dependent types, Multi-stage programming, Partialevaluation, Domain Specific Language Implementation, Resourceaware programming, Functional programming

    1. Introduction

    Multi-stage programming supports separation of concerns in com-piler writing, by allowing automatic program generation to proceedin a series of stages. Each stage captures some new aspect of theproblem space that is then reflected in subsequent stages through

    the program that is generated. A primary advantage of the approachis that it supports the construction of domain specific notations ina nested fashion [11]. Here, each stage allows the encapsulation of

    Permission to make digital or hard copies of all or part of this work for personal orclassroom use is granted without fee provided that copies are not made or distributedfor profit or commercial advantage and that copies bear this notice and the full citationon the first page. To copy otherwise, to republish, to post on servers or to redistributeto lists, requires prior specific permission and/or a fee.

    GPCE06 October 2226, 2006, Portland, Oregon, USA.Copyright c 2006 ACM 1-59593-237-2/06/0010.. . $5.00.

    domain knowledge in a precise way, and programmers may work atdifferent levels (corresponding to stages) according to their degreeof specialisation. For example, in the domain of real-time embed-ded systems which we are investigating, the first stage might be arestricted notation that guaranteed bounded time and space usage,and be used by the applications programmer; the subsequent stagemight be used to define these restricted notations in terms of theunderlying meta-programming system, and be used by the domain

    expert; and the final stage would correspond to the generation ofexecutable code, and be used by the compiler writer.

    A major problem with this approach arises in ensuring that gen-erated programs conform to the properties required by the (meta)-programmer. This problem has been explored in outline by Taha,Sheard and Pasalc amongst others [28, 31, 30], who have pro-duced systems that are capable of correctly preserving type infor-mation across stages. While this is a valuable contribution in reduc-ing runtime type errors for generated programs, these approachesrestrict theexpressivity of their type systems. This approach is valu-able in allowing the automatic verification of types, but verificationof more complex properties will generally require more complexproof structures than can be supported by such frameworks. Forexample, the calculation of bounds on the resources used by a gen-erated program may be essential in a real-time embedded systems

    setting; previous work (e.g. [34, 20]) uses multi-stage programmingto generate resource correct programs, but is limited to specific re-source correctness properties. We are thus motivated to considerhow arbitrary proofs may be embedded within multi-stage pro-grams in a homogeneous framework, in order to allow automaticverification of required program properties for domain specific lan-guages implemented using a multi-stage approach.

    1.1 Overview of our Approach

    Types give a program meaning; dependent type systems, in whichtypes may be predicated on values, allow us to give a more pre-cise type to a program and therefore to be more confident that ithas the intended meaning. In this paper, we consider how the sepa-rate techniques of multi-stage programming and dependently typed

    programming can be combined in order to implement an efficientand correct implementation of a functional programming language.

    We use dependent types to implement a well-typed interpreter,following and extending the ideas of Augustsson and Carlsson [4].We are able to show by construction that the interpreter returns avalue of the correct type and correctly evaluates well-typed terms we take types as the prior notion representing a specification,and use the typechecker to guarantee that our program respects thisspecification. Dependent types ensure, by static checking, that theinterpreter cannot be executed on badly formed or ill-typed code.

  • 8/14/2019 A Verified Staged Interpreter is a Verified Compiler

    2/10

    Thus dependent types provide static guarantees of certain desiredcorrectness properties.

    We further consider the use of staging annotations [32] to controlthe execution order of the interpreter. Staging annotations allowcode generation to be deferred until run-time, when some inputsare known. This means that we can specialise our interpreter forspecific object progams staging the interpreter yields a translator

    from the object language to the meta-language [14]. From here itis a small step to generating a compiler for the object language(for example using offshoring passing the translated code to anexternal compiler [13]). The combination of dependent types andmulti-stage programming therefore gives us a method for buildingverified compilers, for at least some required properties.

    t X X a ?

    i

    @

    type universesA

    j x @

    variableA

    j @ x X t A 3 t @

    function spaceA

    j x X t : t @

    abstractionA

    j t t @

    applicationA

    j

    letx U3 t X t

    int @

    let bindingA

    Figure 1. The core language, T T

    valid ?

    n

    X ?

    n + 1

    T y p e

    @ x X S A P

    x X S

    V a r

    @ x X S U3 s A P

    x X S

    V a l

    f X @ x X S A 3 T s X S

    f s X T s = x

    A p p

    Y x X S e X T @ x X S A 3 T X ?

    n

    x X S : e X @ x X S A 3 T

    L a m

    Y x X S T X ?

    n

    S X ?

    n

    @ x X S A 3 T X ?

    n

    F o r a l l

    e

    1

    X S Y x U3 e

    1

    X S e

    2

    X T

    S X ?

    n

    Y x U3 e

    1

    X S T X ?

    n

    letx X S U3 e

    1

    ine

    2

    X T e

    1

    = x

    L e t

    x X A A

    0

    X ?

    n

    A 9 A

    0

    x X A

    0

    C o n v

    Figure 2. Typing rules forT T

    1.2 The Core Type Theory,T T

    Our implementation language (meta-language) is a strongly nor-malising dependent type theory with inductive families [12], simi-lar to Luos UTT [22] or the Calculus of Inductive Constructions inCOQ [9]. This language, which we call

    T T

    , is an enriched lambdacalculus, with the usual properties of subject reduction, ChurchRosser, and uniqueness of types up to conversion. The strong nor-malisation property (i.e. that evaluation always terminates) is guar-anteed by allowing only primitive recursion over strictly positiveinductive datatypes. The syntax of this language is given in Fig-ure 1, and its typing rules in Figure 2. We may also abbreviate thefunction space

    @ x X S A 3 T

    byS 3 T

    ifx

    is not free inT

    .

    InT T

    , there is a hierarchy of type universes,?

    i

    , where?

    0

    is thetype of types, and

    ?

    n

    X ?

    n + 1

    . We leave universe levels implicit,since they can be inferred by the machine [17]. The key typing ruleswhich set this type system apart from more traditional simply- orpolymorphically-typed

    -calculi are theA p p

    andC o n v

    rules:A p p

    is the rule for applying dependent functions (notex

    may be freein

    T

    , sos

    may be substituted in the type of the application); andC o n v

    is the conversion rule two terms are convertible by the9

    relation if they have a common redex. Checking convertibilityrequires evaluation at compile-time, hence strong normalisation isa requirement for decidable typechecking.

    For clarity of presentation, we will use the higher level E PIGRAMnotation [25], which elaborates to

    T T

    . A more detailed presentationof E PIGRAM can be found in [24];

    T T

    and its compilation schemeare detailed in [6].

    1.3 Programming with Inductive Families

    Inductive families are simultaneously-defined collections of alge-braic data types which can be indexed over values as well as types.For example, we will define a lists with length (or vector) typebelow. We first, however, need to declare a type of natural numbersto represent such lengths:

    dataN

    X ?

    where0 X

    N

    n X

    N

    s n X

    N

    Addition and multiplication can be easily defined by primitive re-cursion. We can now declare vectors as follows:

    V e c t A n

    definesan inductive family of lists indexed over

    A

    , the type of vector ele-ments, and also over

    n

    , the vector length. Note that, by construc-tion,

    only targets vectors of length zero, andx X X x s

    only targetsvectors of length greater than zero:

    data A X ? n X NV e c t A n X ?

    where X V e c t A 0

    x X A x s X V e c t A k

    x X X x s X V e c t A @ s k A

    Note thatA

    andk

    are implicit arguments to the infix constructorX X

    their types can be inferred from the type ofV e c t

    . When thetype includes explicit length information in this way, it follows thatany type-correct function over values in that type must expressthe invariant properties of the length. For example, we can writea bounds-safe list lookup function that gives a static guarantee thata value is never projected from an empty list. In order to do do this,we define a datatype of finite sets, which can be used to representnumbers with an upper bound:

    data n X NF i n n X ?

    wheref 0 X F i n @ s n A

    i X F i n n

    f s i X F i n @ s n A

    Note that there are no elements ofF i n 0

    this would be a finite setwith zero elements. The type of

    l o o k u p

    below expresses staticallythat the bound of the index and the size of the list are the same, sothere can be no run-time error:

    let i X F i n n x s X V e c t A nl o o k u p i x s X A

    l o o k u p i x s @

    elimi @

    casex s

    l o o k u p f 0 @ x X X y s A U3 x

    l o o k u p @ f s j A @ x X X y s A U3 l o o k u p j y s

  • 8/14/2019 A Verified Staged Interpreter is a Verified Compiler

    3/10

    The elim and case notation invoke the primitive recursion andcase analysis operators respectively on

    i

    andx s

    . Termination isguaranteed since these operators are the only means to inspectdata. Unlike a simply typed language, we do not need to giveerror handling cases: the typechecker verifies that the empty vectorcannot be a legal input. We can see this by observing that neitherconstructor of

    F i n

    targets the typeF i n 0

    , therefore no well-typedapplication of

    l o o k u p

    could accept aV e c t A 0

    .

    1.4 Types for Specification

    By giving additional static information about thel o o k u p

    func-tion, we obtain a stronger guarantee of its behaviour from the type-checker. The definition itself, however, is written in the usual way indeed, it is more concise since there is no need for error check-ing. When writing the type, we are really writing a specification.Then, in writing the program, we are at the same time providing aproof that the implementation meets this specification.

    While we accept that such methods may not become widespreadfor general purpose programming in the short term, there are manyimportant applications domains where guarantees of correctnessare vital or desirable. Our own area of interest is resource-bounded

    safety-critical systems [16]; we aim to use the techniques wepresent in this paper to implement a verified compiler for a resourceaware functional language. We will return to this in Section 5.

    1.5 Theorem Proving

    The dependent type system ofT T

    also allows properties to be ex-pressed directly. For example, the following heterogeneous defini-tion of equality, due to McBride [23], is built in to

    T T

    :

    a X A b X B

    a a b X ?

    A X ? a X A

    r e a X a a a

    This definition introduces an infix type constructor,a

    , parametrisedover two types; we can declare equality between any two types, butcan only construct an instance of equality between two definition-ally equal values in the same type. For example,

    r e @ s 0 A

    is aninstance of a proof that

    s 0 a s 0

    . Furthermore, since equality is anordinary datatype just like N and

    V e c t

    , we can also write programsby case analysis on instances of equality, such as the program be-low. This can be viewed as a proof that

    s

    respects equality:

    letp X n a m

    r e s p s p X @ s n A a @ s m A

    r e s p s p @

    casep

    r e s p s @ r e n A U3 r e @ s n A

    We can also represent more complex properties, including logicalrelations:

    datax ; y X

    N

    x y X ?

    wherel e O X 0 y

    p X x y

    l e S p X s x s y

    Note thatx

    andy

    can be left implicit, as their types (and even theirvalues) can be inferred from the type of the relation. For example,l e S @ l e S l e O A

    could represent a proof ofs @ s 0 A s @ s @ s @ s 0 A A

    . Aswith equality, given a proof, we can write programs by recursionover the proof. For example, we can write a safe subtraction func-tion (i.e. the result is guaranteed to be non-negative) by primitiverecursion over the proof that the second argument is less than orequal to the first:

    letn ; m X

    N

    p X m n

    m i n u s n m p X

    N

    m i n u s n m p @

    elimp

    m i n u s n 0 @ l e O n A U3 n

    m i n u s @ s n A @ s m A @ l e S p A U3 m i n u s n m p

    The values for the argumentsn

    andm

    are determined by the

    indices ofl e O

    andl e S

    ; no case analysis on the numbers themselvesis required. The Curry-Howard isomorphism [10, 18] describes thiscorrespondence between proofs and programs.

    The lack of a phase distinction between types and values meansthat we can write proofs like this directly over programs; there isno need to duplicate values at the kind level. While it has beenclaimed elsewhere [8] that this implies that types cannot be erasedat run-time, in fact, we are able to maintain type erasure by insteadestablishing a distinction between compile-time and run-time val-ues using techniques from [6], and erasing those needed at compile-time only.

    1.6 Implementation

    An implementation of the staged interpreter we develop in this

    paper, along with an implementation ofT T

    , is available fromh t t p : / / w w w . d c s . s t - a n d . a c . u k / ~ e b / S T L C /

    .

    2. A Dependently Typed Interpreter

    Dependent types can be used to good effect in the implementationof programming languages. One demonstration of this is Augusts-son and Carlssons well-typed interpreter in Cayenne [4, 3]. Thisinterpreter implements the language given below:

    e X X a a X T : e

    -abstractionj e e

    applicationj a

    variablej e o p e

    binary operatorj n

    numberj b boolean valuej i f e t h e n e e l s e e

    boolean choicej p r i m r e c e e e

    primitive recursion

    o p X X a C j j j

    andj

    or

    T X X a

    N Natural numbersj B o o l

    Booleansj t 3 t

    Function type

    This is a simply typed

    -calculus with booleans and natural num-bers. We call this language

    A C

    ; our implementation augments Au-gustsson and Carlssons with a primitive recursion operator for nat-ural numbers,

    p r i m r e c

    andi f : : : t h e n : : : e l s e

    expressions.

    The typing rules for this language are given in Figure 3, with

    context validity defined as follows:

    valid

    valid ; a X T

    valid

    a X T P ; a X T

    a X T P

    a X T P ; b X S

    The interpreter we present here uses inductive families to repre-sent well-typedness and synchronisation of type and value environ-ments. By using inductive families, we can express explicit rela-tionships between data structures, just like the relationship between

  • 8/14/2019 A Verified Staged Interpreter is a Verified Compiler

    4/10

    n X

    N

    b X B o o l

    a X t P a X t

    ; a X s e X t

    a X s : e X s 3 t

    e

    1

    X s 3 t e

    2

    X s

    e

    1

    e

    2

    X t

    e

    1

    X

    N

    e

    2

    X

    N

    o p P f C j g

    e

    1

    o p e

    2

    X

    N

    e

    1

    X

    N

    e

    2

    X

    N

    o p P f g

    e

    1

    o p e

    2

    X B o o l

    e

    1

    X B o o l e

    2

    X B o o l o p P f

    andj

    org

    e

    1

    o p e

    2

    X B o o l

    x X B o o l t X a f X a

    i f x t h e n t e l s e f X a

    x X

    N

    z X a s X

    N

    3 a 3 a

    p r i m r e c x z s X a

    Figure 3. Typing rules for

    A C

    aV e c t

    and its length. In particular, the types we use express the re-lationship between values and their type and context membership.This means that, without having to prove any theorems, we havestatic guarantees that evaluation preserves type, and that contextlookup will always succeed. Later, we will also see that we can rep-resent raw (untyped) terms and implement typechecking in such away as to guarantee that any well-typed term it produces is of theof the correct type. i.e. we can show soundness of the typecheckingalgorithm, by the types alone.

    2.1 Representation

    We have specified context validity, context membership and thetyping rules in relation to contexts. We would now like to choose

    a representation for A C terms in the meta-language which reflectsthis specification as directly as possible and ensures that only well-typed expressions can be built. In a traditional language some lossof information is inevitable here, but with inductive families we canspecify in the type the relationships between these concepts.

    We begin with a representation of types. These types can be reifiedinto meta-language types with the

    i n t e r p T y

    function, since thedependent type system allows us to compute types:

    dataT y X ?

    whereT y N a t X T y

    T y B o o l X T y

    s ; t X T y

    A r r o w s t X T y

    lett X T y

    i n t e r p T y t X ?

    i n t e r p T y t @

    elimt

    i n t e r p T y T y N a t U3

    N

    i n t e r p T y T y B o o l U3 B o o l

    i n t e r p T y @ A r r o w s t A U3 i n t e r p T y s 3 i n t e r p T y t

    We represent type environments (

    ) as vectors of types, and mem-bership of a type environment as a relation:

    let n X NE n v n X ?

    E n v n U3 V e c t T y n

    dataG X E n v n i X F i n n t X T y

    V a r G i t X ?

    wheret o p X V a r @ s X X G A f 0 s

    v X V a r G i t

    p o p v X V a r @ s X X G A @ f s i A t

    Variables are de Bruijn indexed, and represented by an element ofa finite set. Using finite sets gives an explicit bound on the indexwhich means that the variable can never refer to a value out of thescope given by the type environment. We can also view

    t o p

    andp o p

    as zero and successor constructors of a natural number representinga de Bruijn index.

    If we readV a r G i t

    asG i X t

    , its intended meaning, then wesee a direct correspondence with the earlier definition of contextmembership.

    TheE x p r

    family, which represents expressions in the object lan-guage, is shown below. It is indexed over the type environmentin which it will be evaluated, and the type of value it represents.Therefore any well-typed instance of

    E x p r mustbe a representationof a well-typed term; this is a static guarantee. There is no need

    to supply a well-typing predicate along with an expression as inprevious work [4, 30], since there is no way to construct ill-typedvalues:

    dataG X E n v n A X T y

    E x p r G A X ?

    where k X Ne n a t k X E x p r G T y N a t

    b X B o o l

    e b o o l b X E x p r G T y B o o l

    v X V a r G i t

    e v a r v X E x p r G t

    f X E x p r G @ A r r o w s t A a X E x p r G s

    e a p p f a X E x p r G t

    e X E x p r @ s X X G A t

    e l a m e X E x p r G @ A r r o w s t A

    o p X i n t e r p T y A 3 i n t e r p T y B 3 i n t e r p T y C

    a X E x p r G A b X E x p r G B

    e o p o p a b X E x p r G C

    x X E x p r G B o o l t ; f X E x p r G A

    e i f x t f X E x p r G A

    x X E x p r G

    N

    z X E x p r G A

    s X E x p r G @

    N

    3 A 3 A A

    e p r i m r e c x z s X E x p r G A

    Again, if we read the declarationx X E x p r G T

    with its intended

    meaning,G x X T

    , then we can read the typing rules directlyfrom theE x p r

    type declaration. The only minor exception is thee o p

    constructor, which permits a more generic implementation ofbinary operators. Thus any correctly constructed (i.e. well-typed)instance of an

    E x p r

    must be a representation of a well-typed termin

    A C

    .

    As an example, the factorial function could be defined by primitiverecursion as follows:

    f a c t a x X

    N

    : p r i m r e c x I @ k X

    N

    : i h X

    N

    : @ k C I A i h A

    The representation of this as anE x p r

    is:

  • 8/14/2019 A Verified Staged Interpreter is a Verified Compiler

    5/10

    letx X E x p r G T v e X V a l E n v G

    i n t e r p x v e X i n t e r p T y T

    i n t e r p x v e @

    elimx

    i n t e r p @ e n a t k A v e U3 k

    i n t e r p @ e b o o l b A v e U3 b

    i n t e r p @ e v a r v A v e U3 e n v L o o k u p v v e

    i n t e r p @ e a p p f a A v e U3 @ i n t e r p f v e A @ i n t e r p a v e A

    i n t e r p @ e l a m

    s

    e A v e U3 a X i n t e r p T y s : i n t e r p e @ e x t e n d a v e A

    i n t e r p @ e o p o p a b A v e U3 o p @ i n t e r p a v e A @ i n t e r p b v e A

    i n t e r p @ e i f x t f A v e U3 r u n i f @ i n t e r p x v e A @ i n t e r p t v e A @ i n t e r p f v e A

    i n t e r p @ e p r i m r e c x z s A v e U3 p r i m r e c @ i n t e r p x v e A @ i n t e r p z v e A @ i n t e r p s v e A

    letx X B o o l t ; f X A

    r u n i f x t f X A

    r u n i f x t f @

    casex

    r u n i f t r u e t f U3 t

    r u n i f f a l s e t f U3 f

    let n X N z X A s X N 3 A 3 Ap r i m r e c n z s X A

    p r i m r e c n z s @

    elimn

    p r i m r e c 0 z s U3 z

    p r i m r e c @ s k A z s U3 s k @ p r i m r e c k z s A

    Figure 4. The interpreter

    f a c t a e l a m @ e p r i m r e c t o p @ e n a t @ s 0 A A

    @ e l a m @ e l a m

    @ e o p m u l t @ e o p p l u s

    @ e v a r @ p o p t o p A A @ e n a t @ s 0 A A A @ e v a r t o p A A A A A

    The interpreter has a value environment in which to look up thevalues of variables. Since variables in the environment may havedifferent types, using a

    V e c t

    is not appropriate. Instead, we definea type which synchronises the value environment with the typeenvironment; each value in the value environment gets its type fromthe corresponding entry in the type environment. The declaration ofthe value environment and a lookup function are as follows:

    data G X E n v nV a l E n v G X ?

    wheree m p t y X V a l E n v

    t X i n t e r p T y T r X V a l E n v G

    e x t e n d t r X V a l E n v @ T X X G A

    let v X V a r G i T v e X V a l E n v Ge n v L o o k u p v v e X i n t e r p T y T

    e n v L o o k u p v v e @

    elimv

    e n v L o o k u p t o p @ e x t e n d t r A U3 t

    e n v L o o k u p @ p o p v A @ e x t e n d t r A U3 e n v L o o k u p v r

    The type environment helps to ensure that we can only representwell-typed terms. The value environment is used with the inter-preter to ensure that any value we project out of the environmenthas the correct type. The invariants on

    e n v L o o k u p

    guarantee thatwe can never project a non-existent value out of the environment. Itis not possible to project a value from the empty environment; suchan operation would be ill-typed. This means that there is no need

    for any error checking in the interpreter; we know by constructionthat all possible inputs are well-typed and that the output will be ofthe correct type.

    2.2 The Interpreter

    The implementation of the interpreter is shown in Figure 4.i n t e r p

    is written by structural recursion over the input expressionx

    . It re-turns a semantic representation, as a

    T T

    term, of the input. So,for example, the interpretation of a

    -abstraction (e l a m

    ) is aT T

    function which implements that

    -abstraction. Interpretation of anapplication then simply applies the function to the interpretation of

    its argument. Note that in the case fore l a m

    , we use the implicit ar-guments

    to establish the input type of the function. This approachis similar to normalisation by evaluation [5] in that we constructa semantic representation of the term to be interpreted. It is also atagless interpreter; i.e., there is no need to tag the return value withits type, since the the type is known from the index of the input.

    Evaluating thef a c t

    function defined above gives, as expected, themeta-level implementation of factorial by primitive recursion1:

    f a c t a x X

    N

    : p r i m r e c x @ s 0 A @ k ; i h X

    N

    : m u l t @ p l u s k @ s 0 A A i h A

    3. Dependent Types and Staging

    In [33], Taha states that a staged interpreter is a translator. Hisidea is to defer code generation for staged fragments until run-time;in the case of an interpreter, the program generates a meta-languageimplementation of the object program, eliminating the interpreta-tion overhead. In this section, we apply Tahas staging techniqueto the

    T T

    language and show how we can modify our dependentlytyped interpreter to take advantage of staging annotations. A majoradvantage over Tahas approach is that dependent types allow usto maintain the compile-time correctness guarantees of Section 2automatically. It follows that, by construction, our object programsare well-typed, well-scoped and terminating.

    3.1 Extensions toT T

    To add staging constructs toT T

    , we begin by extending it withthe notion oflevels. Level 0 is the run-time execution level; higherlevels contain deferred code. We index the typing judgment with

    the level n , i.e. n x X A states that x has type A at level n . Weextend

    T T

    with the following expression forms:

    e X X a : : :

    j

    e

    (Quoted term)j h e i

    (Code type)j 3 e

    (Evaluate term)j $ e

    (Escape term)

    e

    quotes an expressione

    , deferring its execution until the nextstage. This lifts the expression from level

    n

    to leveln C I

    1 In fact p r i m r e c , m u l t and p l u s are also unfolded, but we omit thesedetails for clarity.

  • 8/14/2019 A Verified Staged Interpreter is a Verified Compiler

    6/10

    h e i

    is the code type of a quoted term.

    3 e

    evaluates a quoted expressione

    . Since this executes code, itis only valid at level 0, where

    e

    has no free variables.

    $ e

    splices (escapes) a quoted term into a lower level. Thismoves an expression from level

    n C I

    to leveln

    ;n

    cannot belevel 0, since this would imply execution.

    The typing rules are given in Figure 5. These typing rules addition-ally ensure staging correctness in particular, a value bound ina later stage cannot be used in an earlier stage. To guarantee this,context entries are annotated with the level where they are boundand checked at the point of use, with updated

    V a r

    andV a l

    rules,and code can only be executed if it has no free variables. This isa more restrictive solution than strictly necessary we could forexample use environment classifiers [35] to ensure that all free vari-ables have an originating environment but is sufficient for manyprograms including those we present in this paper.

    n + 1

    e X A

    n

    e X h A i

    Q u o t e

    n

    A X ?

    n

    h A i X ?

    C o d e

    0

    e X h A i

    0

    3 e X A

    E v a l

    n

    e X h A i

    n + 1

    $ e X A

    E s c a p e

    x X

    n

    S P n m

    m

    x X S

    V a r

    x U3 s X

    n

    S P n m

    m

    x X S

    V a l

    Figure 5. Typing rules for staging constructs

    The two basic notions of equivalence are:

    e X A

    3 @

    e A 9 e

    e X A

    $ @

    e A 9 e

    The distinction between the escape and evaluation constructs is thatescape must be enclosed by quotation brackets and evaluation ap-plies only to closed terms the purpose of escape is to allow re-duction inside a code building context. Extending an implementa-tion of

    T T

    with these constructs is fairly straightforward; the oneaspect needing care is conversion of quoted terms:

    x ; y X A x 9 y

    x 9

    y

    This rule states that quoted terms are definitionally equal if the val-ues they quote are definitionally equal. An implementation of con-version which simply compares normalforms would not implementthis rule correctly, so should be extended accordingly.

    3.2 Example p o w e r

    A classic example of the benefit of multi-stage programming is thep o w e r

    function, which returns then

    th power ofm

    . We can definethis as follows:

    letn ; m X

    N

    p o w e r n m X

    N

    p o w e r n m @

    elimn

    p o w e r 0 m U3 s 0

    p o w e r @ s k A m U3 m @ p o w e r k m A

    This is a generic power function, in that it can be used to compute avalue raised to any non-negative exponent. However, there is a priceto pay for genericity; if the function is often used with the samevalue of

    n

    , our program still has to perform the work associatedwith this input on every application.

    We could, at compile time, create specialised instances of thisfunction for commonly used inputs, e.g.:

    p o w e r 2 U3 p o w e r @ s @ s 0 A A

    p o w e r 4 U3 p o w e r @ s @ s @ s @ s 0 A A A A

    These can be partially evaluated at compile-time, but this requiresknowing in advance what the specialised inputs are. What if we donot know until run-time? We do not have a way, in the languagewithout staging annotations, of partially evaluating at run-time tocreate these specialised instances on dynamic data.

    Staging annotations, combined with run-time code generation, giveus a method for creating these specialised instances dynamically:

    letn X

    N

    m X h

    N

    i

    p o w e r

    0

    n m X h

    N

    i

    p o w e r

    0

    n m @

    elimn

    p o w e r

    0

    0 m U3

    @ s 0 A

    p o w e r

    0

    @ s k A m U3

    @ $ m $ @ p o w e r

    0

    k m A A

    Now, reading an input at run-time, we dynamically generate aspecialised

    p o w e r

    function; for example, with the inputs @ s 0 A

    :

    p o w e r

    0

    @ s @ s 0 A A a m X h

    N

    i :

    @ m m @ s 0 A A

    We run such generated code with the3

    construct. For example,p o w e r 2

    can now be defined as follows:

    p o w e r 2 a 3 @

    m X

    N

    : $ @ p o w e r

    0

    @ s @ s 0 A A

    m A A

    The3

    construct compiles and executes its argument; at run-time,this will generate code for a specialised power of two function.This behaves in the same way as our earlier unstaged definitionof

    p o w e r 2

    but with the partial evaluation deferred until run-time.

    Although this is a somewhat artificial example, it illustrates thedifference between specialising functions at compile-time and run-time. This staging technique has greater benefit where we have alarge, commonly used structure which is nevertheless not knownuntil run-time for example, a syntax tree for a program which isto be interpreted.

    3.3 A Staged Interpreter

    Partial evaluation of an object program yields a representation ofthe program in the meta-language; effectively this is a translatedversion of the object program. If we can defer this partial evalua-

    tion until run-time, then we do not need to know the object programuntil run-time. This allows us to remove the interpretation overheadand thus generate a translatorfor the object language. In the pre-vious section, we saw that staging a program allowed us to deferpartial evaluation of the

    p o w e r

    function until run-time. We willnow consider how to apply this technique to the interpreter of Sec-tion 2.2. Figure 6 gives a staged version of the interpreter. The basicdefinition is as before, but with staging annotations added to denotewhere code generation should be deferred until run-time. Note thatthere are no staging annotations on the call to

    e n v L o o k u p

    . Thisis because we would also like to partially evaluate this function thus projection of variables from the value environment is done

  • 8/14/2019 A Verified Staged Interpreter is a Verified Compiler

    7/10

    letx X E x p r G T v e X V a l E n v G

    i n t e r p x v e X h i n t e r p T y T i

    i n t e r p x v e @

    elimx

    i n t e r p @ e n a t k A v e U3

    k

    i n t e r p @ e b o o l b A v e U3

    b

    i n t e r p @ e v a r v A v e U3 e n v L o o k u p v v e

    i n t e r p @ e a p p f a A v e U3

    @ $ @ i n t e r p f v e A $ @ i n t e r p a v e A A

    i n t e r p @ e l a m

    s

    e A v e U3

    @ a X i n t e r p T y s : $ @ i n t e r p e @ e x t e n d a v e A A A

    i n t e r p @ e o p o p a b A v e U3

    @ o p $ @ i n t e r p a v e A $ @ i n t e r p b v e A A

    i n t e r p @ e i f x t f A v e U3

    @ r u n i f $ @ i n t e r p x v e A $ @ i n t e r p t v e A $ @ i n t e r p f v e A A

    i n t e r p @ e p r i m r e c x z s A v e U3

    @ p r i m r e c $ @ i n t e r p x v e A $ @ i n t e r p z v e A $ @ i n t e r p s v e A A

    letn X

    N

    t ; f X A

    r u n i f n t f X A

    r u n i f n t f @

    casen

    r u n i f t r u e t f U3 t

    r u n i f f a l s e t f U3 f

    let n X N z X A s X N 3 A 3 Ap r i m r e c n z s X A

    p r i m r e c n z s @

    elimn

    p r i m r e c 0 z s U3 z

    p r i m r e c @ s k A z s U3 s k @ p r i m r e c k z s A

    Figure 6. The staged interpreter

    only once. We stagee n v L o o k u p

    as follows, storing code in thevalue environment:

    data G X E n v nV a l E n v G X ?

    wheree m p t y X V a l E n v

    t X h i n t e r p T y T i r X V a l E n v G

    e x t e n d t r X V a l E n v @ T X X G A

    let v X V a r G i T v e X V a l E n v Ge n v L o o k u p v v e X h i n t e r p T y T i

    e n v L o o k u p v v e @

    elimv

    e n v L o o k u p t o p @ e x t e n d t r A U3 t

    e n v L o o k u p @ p o p v A @ e x t e n d t r A U3 e n v L o o k u p v r

    Evaluation of thef a c t

    function gives a quoted representation of themeta-level implementation of factorial, with

    p r i m r e c

    ,m u l t

    andp l u s

    not inlined:

    f a c t a

    @ x X

    N

    : p r i m r e c x @ s 0 A @ k ; i h X

    N

    : m u l t @ p l u s k @ s 0 A A i h A A

    We have now deferred the partial evaluation until run-time ratherthan incurring interpretation overhead each time we wish to runthis object language function, there is now a single compilationoverhead, with code generated for the quoted function at run-time.

    4. A Verified Compiler

    Our choice of data type for the object language means that the im-plementation of the interpreter is simultaneously a proof of the de-sired properties. By using full-spectrum dependent types, we have

    a guarantee that input to the interpreter is well-scoped and well-typed, and that the output will be the correct type. Our represen-tation of the object language, in particular the invariants which itsatisfies, means that we know the properties that the interpretersatisfies by construction rather than by post-hoc theorem proving.Staging lets us take this a step further from the representation ofthe object language and the staging of the interpreter, we generatea correct compiler, by construction.

    In this section, we will give some desired properties of our stagedinterpreter, and give explicit proofs of these properties. The princi-pal advantage of our method for compiler construction is that theseproofs are extremely simple the use of inductive families and our

    choice of invariants on these families means that the properties inwhich we areinterested arealready checked by the (meta-language)typechecker.

    data n X NR a w n X ?

    where k X Nr n a t k X R a w n

    b X B o o l

    r b o o l b X R a w n

    i X F i n n

    r v a r i X R a w n

    f ; a X R a w n

    r a p p f a X R a w n

    e X R a w @ s n A

    r l a m e X R a w n

    o p X i n t e r p T y A 3 i n t e r p T y B 3 i n t e r p T y C

    a ; b X R a w n

    r o p o p a b X R a w n

    x ; t ; f X R a w n

    r i f x t f X R a w n

    x ; z ; s X R a w n

    r p r i m r e c x z s X R a w n

    dataG X E n v n r X R a w n T X T y

    C h e c k e d G r T X ?

    wheree X E x p r G T

    o k e X C h e c k e d G r T e r r o r X C h e c k e d G r T

    letG X E n v n r X R a w n T X T y

    c h e c k G r T X C h e c k e d G r T

    Figure 7. Checking Raw Terms

    Let us first complete our prototype implementation with a datatypefor raw terms (we assume the existence of a parser which generatesthese well-scoped terms) and a typechecker. The

    c h e c k

    function(declared in Figure 7) takes a raw term and its intended type andreturns a

    C h e c k e d

    structure, which either contains a well-typedterm, or an error. We omit the definition of

    c h e c k

    ; its structurefollows that of the typechecker example in [25].

    To begin, let us define a correspondence between the definition of

    A C

    and our representation,R a w

    ,T y

    andE x p r

    .

  • 8/14/2019 A Verified Staged Interpreter is a Verified Compiler

    8/10

    Definition 1 (Representation). If t X T

    then:

    is the representation of the context (withn

    entries) as anE n v n

    T is the representation of the type as a T y .

    t

    is the representation of the term as anE x p r

    T

    .

    RAW @ t A is the representation of the term as raw syntax,R a w n

    .

    Since dependent types encode many correctness properties directlywithin the program, the substantial part of any compiler correctnessproof is in checking that the implementation faithfully models thesemantics. We can show that our representation is a complete andfaithful representation of

    A C

    by its correspondence with the typingrules.

    Lemma 2 (Soundness of representation). If e X E x p r T ,then there is a term

    t

    such thatt

    a e

    and t X T

    .

    Proof. Each constructor of E x p r directly corresponds to a typingrule (Figure 3), by reading

    x X E x p r G T

    asG x X T

    .

    Lemma 3 (Completeness of representation). If t X T

    , then

    there is a term e X E x p r T , such that t a e .

    Proof. Each typing rule (Figure 3), directly corresponds to a con-structor of

    E x p r

    by readingx X E x p r G T

    asG x X T

    .

    For the correctness of our implementation, we verify that the type-checker is a sound and complete implementation of the typingrules, and that evaluation preserves type.

    Theorem 4 (Soundness of typechecking function). Ifc h e c k

    RAW@ t A

    T

    ;o k e

    then t X T

    , wheree X E x p r

    T

    .

    Proof. By the type of theC h e c k e d

    view, and thec h e c k

    function,no

    e

    can be constructed which does not have the typeE x p r T .

    Then t X T

    by Lemma 2.

    Although we can show soundness using types alone, completenessrequires some extra work, involving the details of the

    c h e c k

    func-tion.

    Theorem 5 (Completeness of typechecking function). If t X T

    thenc h e c k

    RAW@ t A

    T

    ;o k e

    , wheree X E x p r T .

    Proof Sketch. By Lemma 3, there exists a term e X E x p r T .The totality of the

    c h e c k

    function ensures that it will produceeither a term

    o k e

    , wheree

    represents a term of the correct type(by Theorem 4), or

    e r r o r

    .

    To show thatc h e c k

    produces a well-typed term where it exists, we

    define a forgetful map operation j e j , where e X E x p r G T , andshow by induction over

    j e j

    thatc h e c k G j e j T

    ;

    o k e

    .

    Theorem 6 (Subject Reduction). If i n t e r p s v e ; t ands X E x p r G T

    thent X T

    .

    Proof. The return type of i n t e r p ensures that the meta-level valuereturned has the type required by its representation. By typecheck-ing in

    T T

    ,s X E x p r G T

    ensures thatt X T

    . Furthermore, sincei n t e r p

    is total, evaluation will always return a value.

    In addition, because the implementation language is strongly nor-malising (i.e., evaluation always terminates with a constructorheaded value), we know that variable lookup will always suc-ceed, and the interpreter will always terminate correctly. There-fore, for any object program, the interpreter will always produce ameta-level representation of that program. We have verified theseproperties for the unstaged implementation since the stagingannotations guarantee that types are preserved between stages, andthese properties are shown by the programs type, we can be surethat these correctness properties are preserved after staging the in-terpreter. By verifying the properties for the interpreter, and addingstaging annotations, we have verified the properties for the result-ing compiler.

    4.1 Termination and Side Effects

    Since we have used a strongly normalising language to implementa staged interpreter for

    A C

    , we can be sure of strong propertiessuch as termination of

    A C

    . For many domain specific languages,guaranteed termination is important. For example Hume [16] isseparated into two layers, a co-ordination layer which directs theflow of data, and a computation layer which processes data. If wedesire strong static guarantees about programs in the computation

    layer, we may also require computations to be total.From our perspective,the primary benefits of a strongly terminatinglanguage, in which

    c

    is not a value, are that proofs are total andtypechecking is decidable. In a full-spectrum dependently typedlanguage, where values can appear in types and vice versa, thetermination property is essential for decidability of typechecking.Furthermore, without it, it is possible to construct a proof of anyproposition by creating a non-terminating function

    b o t t o m a

    b o t t o m

    , which can have any type we like.

    As far as proofs and typechecking are concerned, totality is vital.However, the resulting limitation is that our programs must also notpossess side-effects, such as I/O, since these effects could appearin types. This precludes the implementation of a Turing completelanguage in

    T T

    as it stands. Meta-D [30] avoids this problem byrestricting the type language. We prefer not to make the same re-strictions since a key part of our method is that proofs are easilyexpressible within the source language. Our proposed solution tothis problem is as for I/O in Haskell, treating partiality as a monad.In collaboration with Capretta, Altenkirch [1] and Uustalu [36] aredeveloping such a method for introducing partiality into a depen-dently typed language without losing decidable typechecking, andthis work transfers neatly to our setting.

    5. Dependent Types for Resource Analysis

    We have previously considered the use of dependent types to pro-vide static guarantees of resource properties for functional pro-grams [7]. The idea is to predicate each user-defined type on anatural number, representing the size of values in that type; eachfunction then returns both a value and its size plus a proof that thesize satisfies a given predicate. This is represented by the followingS i z e

    type:

    dataA X

    N

    3 ? P X @ n X

    N

    A 3 A n 3 ?

    S i z e A P X ?

    wherev a l X A n p X P n v a l

    s i z e v a l p X S i z e A P

    For example, we can implement a size-safe list append functionby predicating a

    L i s t

    type on a natural number, and implementinga p p e n d

    as follows:

  • 8/14/2019 A Verified Staged Interpreter is a Verified Compiler

    9/10

    data A X N 3 ?L i s t

    S

    A X

    N

    3 ?

    wheren i l

    S

    X L i s t

    S

    A 0

    x X A x n x s X L i s t

    S

    A x s n

    c o n s

    S

    x x s X L i s t

    S

    A @ s x s n A

    letx s X L i s t

    S

    A x s n y s X L i s t

    S

    A y s n

    a p p e n d x s y s X S i z e L i s t

    S

    A @ n X

    N

    : v X L i s t

    S

    A n :

    n a x s n C y s n A

    a p p e n d n i l

    S

    y s U3 s i z e y s @ r e y s n A

    a p p e n d @ c o n s

    S

    x x s A y s

    U3

    let@ s i z e v a l p A a a p p e n d x s y s

    ins i z e @ c o n s

    S

    x v a l A @ r e s p s p A

    We have not yet considered in detailhow to execute these programs.It is important when constructing a resource analysis that the costproofs do not interfere with the actual program execution costs,otherwise the costs will no longer be valid! While the techniquesof [6] will mitigate this problem, they will not completely removethis dynamic cost information. A further (potentially error-prone)pass is required in the implementation in order to translate

    S i z e

    structures into simple values. A staged interpreter for a resource-aware functional language would, however, allow us to remove thecost information in a principled way.

    The verified interpreter presented above can now be extended toinclude size properties as well as type properties. To do this, weextend the type environment to include size variables as well as

    -bound variables, as shown in Figure 8. Since type environ-ments now contain size information, we can embed size proper-ties in types and proofs of those properties within programs, asrequired. We are continuing to develop this idea for a full sizeaware language, using staging to generate

    T T

    code for executionvia Hume [16].

    datav

    n

    ; v

    s

    X

    N

    T y E n v v

    n

    v

    s

    X ?

    wheree m p t y X T y E n v 0 0

    G X T y E n v v

    n

    v

    s

    s i z e E x t e n d G X T y E n v v

    n

    @ s v

    s

    A

    t X T y v

    s

    G X T y E n v v

    n

    v

    s

    t y p e E x t e n d t G X T y E n v @ s v

    n

    A v

    s

    Figure 8. Type environments with size variables

    6. Related Work

    Our work brings together the related areas of dependently typedprogramming [2] and multi-stage programming [32], buildingon the idea of a tagless staged interpreter [30, 29]. One impor-

    tant difference in our work is the use of full-spectrum dependenttypes [26]; there is no syntactic distinction between types andterms. An advantage of this is that it becomes easier to expressproperties of a program in the type, without the need to duplicatevalues at the kind level. We do not need to maintain a phase dis-tinction between types and values; instead, we maintain a phasedistinction between compile-time and run-time values using tech-niques originally developed in Bradys PhD thesis [6].

    Furthermore, our language does not have effects such as non-termination. This is important if we want our programs to have veri-fiable static guarantees; non-termination (via a fixpoint combinator

    with type@ P X ? A 3 @ P 3 P A 3 P

    ) introduces an incon-sistency into our programs which means that proofs of propertiesin the program can no longer be trusted. Further examples of pro-gramming in this way, including the rationale, are presented in [2].By adding staging annotations to a logically sound type theory weaim to implement compilers with verifiable static guarantees.

    Our approach to verification is to annotate data structures with their

    invariants. Another possibility with a dependent type system is topair a program with a proof of its specification. This is the approachtaken by Leroy [21], who is developing and certifying a compilerin C OQ. Hutton and Wright [19] also discuss compiler correctness,but providing an external proof of the required properties. McK-inna and Wright [27] use E PIGRAM to give an explicit proof ofcompiler correctness in the implementation language. These meth-ods concentrate on the back-end; our approach, assuming a correctcompiler for the meta-language, allows us to concentrate on the se-mantics of the object language.

    We have not yet addressed the implementation techniques requiredof our multi-stage language. Our current prototype implementationis based on normalisation by evaluation [5]. The MetaOCaml com-piler implements the

    3

    (eval) construct by linking the compiler inwith the executable and representing code as an abstract syntax

    tree2

    . A more practical implementation technique may be to ex-ploit the relationship with Gregoire and Leroys compiled strongreduction [15] one way of understanding staging annotations isthat they direct when to reduce under binders. Gregoire and Leroystechnique, in which they extend run-time values with a represen-tation of free variables, may provide an efficient method for con-structing code at run-time.

    7. Conclusions and Further Work

    This paper has investigated the construction of verified compilersfor domain specific languages using a combination of dependenttypes and multi-stage programming. While the use of dependenttypes to expose software properties means that some initial workmust be done in order to properly express these properties in the

    types, there is a significant benefit in improving the ease of subse-quent verification, and in providing support for automation. More-over, once constructed, a single dependently typed framework maybe reused in either a general or domain specific manner.

    We have taken great care to annotate the representation of theinterpreter data structures in such a way as to guarantee that animplementation of the interpreter is both total and preserves thetype. The payoffs are that the implementation is straightforward,no error checking is required, and that the typechecker guaranteesthe properties we specify. The rationale behind working so hardto choose the right representation is that data is static and code isdynamic if we choose to annotate the static construct with moreinformation, we do less work dynamically. This greatly simplifiesprogram verification the only work we have to do is to show thatthe data structure we use accurately models the requirements.

    This paper has considered only fairly simple properties. In a realdomain specific language, such as our Hume target [16], we willneed to express both more complex invariants and more complexproperties than the simple size metrics used here. in such a setting,full spectrum dependent types will be invaluable in allowing us toexpress the relationship between the language and its propertiesprecisely. Such properties might include: guaranteed termination;time, space and power usage for some implementation; or thecorrectness of specific program transformations. Adding staging

    2 Walid Taha, pers. comm.

  • 8/14/2019 A Verified Staged Interpreter is a Verified Compiler

    10/10

    annotations to such a domain specific interpreter will give a meta-language implementation, without explicit proofs, but preservingthe guarantees in the interpreters type.

    While partial evaluation of a strongly normalising program canyield a semantic representation of an object program in the metalanguage without any need for adding staging annotations, we doget an important further benefit from adding these annotations

    namely, that we obtain a machine code representation of the objectprogram, in addition to the meta-language code provided by partialevaluation. By combining the two techniques of dependently typedprogramming and multi-stage programming, we can implement anefficient compiler for a resource aware functional language withstrong static guarantees.

    Acknowledgements

    This work is generously supported by EPSRC grant EP/C001346/1and by EU Framework VI IST-510255 (EmBounded). We wouldlike to thank Walid Taha and James McKinna, and the anonymousreferees for their helpful comments.

    References[1] T. Altenkirch. Stop thinking about bottoms when writing programs,

    2006. Talk at BCTCS 2006.

    [2] T. Altenkirch, C. McBride, and J. McKinna. Why dependent typesmatter.

    h t t p : / / w w w . c s . n o t t . a c . u k / ~ t x a / p u b l / y d t m . p d f

    ,2005. Draft.

    [3] L. Augustsson. Cayenne - a language with dependent types. In Proc.1998 International Conf. on Functional Programming (ICFP 98),pages 239250, 1998.

    [4] L. Augustsson and M. Carlsson. An exercise in dependenttypes: A well-typed interpreter. h t t p : / / w w w . c s . c h a l m e r s . s e / ~ a u g u s t s s / c a y e n n e / , 1999.

    [5] U. Berger and H. Schwichtenberg. An inverse of the evaluationfunctional for typed

    -calculus. In R. Vemuri, editor, Proc. 1991IEEE Symp. on Logic in Comp. Sci., pages 203211, 1991.

    [6] E. Brady. Practical Implementation of a Dependently TypedFunctional Programming Language. PhD thesis, University ofDurham, 2005.

    [7] E. Brady and K. Hammond. A dependently typed framework forstatic analysis of program execution costs. In Proc. Implementationof Functional Languages (IFL 2005). Springer, 2006.

    [8] L. Cardelli. Phase distinctions in type theory. Manuscript, 1988.

    [9] Coq Development Team. The Coq proof assistant referencemanual.

    h t t p : / / c o q . i n r i a . f r /

    , 2001.

    [10] H. B. Curry and R. Feys. Combinatory Logic, volume 1. NorthHolland, 1958.

    [11] K. Czarnecki, J. ODonnell, J. Striegnitz, and W. Taha. DSLimplementation in MetaOCaml, Template Haskell, and C++. In

    Domain Specific Program Genearation 2004, volume 3016 ofLNCS.

    Springer, 2004.[12] P. Dybjer. Inductive families. Formal Aspects of Computing,

    6(4):440465, 1994.

    [13] J. Eckhardt, R. Kaibachev, E. Pasalc, K. Swadi, and W. Taha.Implicitly heterogeneous multi-stage programming. In Proc. 2005Conf. on Generative Programming and Component Engineering(GPCE 2005), volume 3676 ofLNCS. Springer, 2005.

    [14] Y. Futamura. Partial evaluation of computation process an approachto a compiler-compiler. Higher-Order and Symbolic Computation,12(4), 1999. Reprinted from Systems

    Computers

    Controls 2(5),1971.

    [15] B. Gregoire and X. Leroy. A compiled implementation of strongreduction. In Proc. 2002 International Conf. on FunctionalProgramming (ICFP 2002), pages 235246, 2002.

    [16] K. Hammond and G. Michaelson. Hume: a Domain-SpecificLanguage for Real-Time Embedded Systems. In Proc. Conf.Generative Programming and Component Engineering (GPCE 03),Lecture Notes in Computer Science. Springer-Verlag, 2003.

    [17] R. Harper and R. Pollack. Type checking with universes. TheoreticalComputer Science, 89(1):107136, 1991.

    [18] W. A. Howard. The formulae-as-types notion of construction. InJ. P. Seldin and J. R. Hindley, editors, To H.B.Curry: Essays oncombinatory logic, lambda calculus and formalism. Academic Press,1980. A reprint of an unpublished manuscript from 1969.

    [19] G. Hutton and J. Wright. Compiling exceptions correctly. In Mathematics of Program Construction, volume 3125 of LNCS.Springer, 2004.

    [20] O. Kiselyov, K. Swahi, and W. Taha. A methodology for generatingverified combinatorial circuits. In Fourth International C onferenceon Embedded Software, pages 249258. ACM, 2004.

    [21] X. Leroy. Formal certification of a compiler back-end. In Principlesof Programming Languages 2006, pages 4254. ACM Press, 2006.

    [22] Z. Luo. Computation and Reasoning A Type Theory for Computer

    Science. Intl. Series of Monographs on Comp. Sci. OUP, 1994.[23] C. McBride. Dependently Typed Functional Programs and their

    proofs. PhD thesis, University of Edinburgh, May 2000.

    [24] C. McBride. Epigram: Practical programming with dependent types.Lecture Notes, International Summer School on Advanced FunctionalProgramming, 2004.

    [25] C. McBride and J. McKinna. The view from the left. Journal ofFunctional Programming, 14(1):69111, 2004.

    [26] J. McKinna. Why dependent types matter. In Proc. ACM Symp.on Principles of Programming Languages (POPL 2006), pages 11,2006.

    [27] J. McKinna and J. Wright. A type-correct, stack-safe, provablycorrect expression compiler in Epigram. Journal of FunctionalProgramming, 2006. To appear.

    [28] MetaOCaml: A compiled, type-safe multi-stage programming

    language. Available online from h t t p : / / w w w . c s . r i c e . e d u / ~ t a h a / M e t a O C a m l / , 2001.

    [29] E. Pasalc. The Role of Type-Equality in Meta-programming. PhDthesis, OGI School of Science and Engineering, 2004.

    [30] E. Pasalc, W. Taha, and T. Sheard. Tagless staged interpreters fortyped languages. In Proc. 2002 International Conf. on FunctionalProgramming (ICFP 2002). ACM, 2002.

    [31] T. Sheard and S. Peyton-Jones. Template meta-programming forhaskell. In Proc. 2002 ACM Haskell workshop, pages 116, 2002.

    [32] W. Taha. Multi-stage Programming: Its Theory and Applications.PhD thesis, Oregon Graduate Inst. of Science and Technology, 1999.

    [33] W. Taha. A gentle introduction to multi-stage programming,2003. Available from

    h t t p : / / w w w . c s . r i c e . e d u / ~ t a h a /

    p u b l i c a t i o n s / j o u r n a l / d s p g 0 4 a . p d f .

    [34] W. Taha, S. Ellner, and H. Xi. Generating heap-bounded programs ina functional setting. In Third International Conference on EmbeddedSoftware, volume 2855 ofLNCS. Springer, 2003.

    [35] W. Taha and M. F. Nielsen. Environment classifiers. In Proc. ACMSymp. on Principles of Programming Languages (POPL 2003), pages2637, 2003.

    [36] T. Uustalu. Partiality is an effect, 2004. Talk at Dagstuhl workshopon Dependently Typed Progamming.