1 meta-programming through typeful code representation chiyan chen and hongwei xi boston university

Post on 19-Dec-2015

223 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

1

Meta-Programming through

Typeful Code Representation

Chiyan Chen and Hongwei Xi

Boston University

2

Talk Overview

Meta-Programming Examples Untyped (Scheme) Typed (MetaML)

Some Issues on Typing Meta-ProgramsTypeful Code Representation Higher-Order Abstract Syntax First-Order Abstract Syntax (via de Bruijn

indexes)A Translation to Provide Syntactic Support for Meta-Programming

3

A Meta-Program in Scheme

(define (run code) (eval code nil))

(define (power n x)(if (= n 0) 1 `(* ,x ,(power (- n 1) x))))

;;; (power 2 ‘x) yields ( x ( x 1))(define square

(run `(lambda (x) ,(power 2 ‘x))))

;;; (power 3 ‘y) yields (y ( y ( y 1)))(define cube

(run `(lambda (y) ,(power 3 ‘y))))

4

A Meta-Program in MetaML

(* <> : the type for code of type (* run is a built-in function of type: <> *)

fun power (n: int) (x: <int>): <int> =if n = 0 then <1>else <~x ~(power (n-1) x)>

val square: int int = run <fn x => ~(power 2 <x>)>

val cube: int int = run <fn y => ~(power 3 <y>)>

5

Scheme vs. MetaML

Scheme is untyped but MetaML is typed The type system of MetaML can be of great use in

ruling out ill-staged meta-programs

The syntax supporting meta-programming can be “translated away” in Scheme but not in MetaML

For instance,

(define (power n x) (if (= n 0) 1 `(,x ,(power (- n 1) x))))

can really be defined as follows:

(define (power n x) (if (= n 0) 1 (list ‘ x (power (- n 1) x))))

6

Higher-Order Abstract Syntax

In essence, it uses functions in the meta-language to represent functions in the object-language

For instance, we can declare a datatype in ML as follows to represent pure untyped lambda-expressions:

datatype exp = Lam of (exp exp) | App of exp exp

As an example, the -expression x. y.y(x) is represented as follows in ML:

Lam(fn x => Lam (fn y => App (y, x)))

7

Typeful H.O.A.S.

HOAS: a unary type constructor() HOAS: the type for the representation of a closed object-

program of type HOASlam: .. ( HOAS HOAS) ( ) HOAS

HOASapp: .. ( ) HOAS HOAS HOAS

For instance, the simply typed -expression x:int. y:int int.y(x)is represented as follows:

HOASlam(fn x: int HOAS => HOASlam (fn y: (int int) HOAS => HOASapp (y, x)))

which is given the type (int (int int) int) HOAS.

8

Weak Head Normalization

As an example, we can implement the following function whnf tocompute the weak head normal form of a given -expression:

fun whnf (t as HOASlam _) = t| whnf (HOASapp (t1, t2)) = (case whnf t1 of HOASlam f => whnf (f t2) | t1 as HOASapp _ => HOASapp (t1, t2))

withtype {‘a}. ‘a HOAS ‘a HOAS

The invariant that weak head normalization is type-preserving iscaptured here.

Notice that HOASlam(whnf) does not represent any -expression (of type (‘a ‘a) HOAS )

9

A Difficulty with H.O.A.S.

With h.o.a.s., it often seems inherently difficult to represent object-programs containing free variablesOn the other hand, it also seems that there is a genuine need for dealing with open code in meta-programming For instance, in the following MetaML

program: <fn x => ~(power 2 <x>)>the expression power 2 <x> is open code.

10

Free Variable Evaluation

With h.o.a.s., there is a difficult issue involving compiling/evaluating object-programs containing free variables. For instance, this issue occurs when we

compile the following object-program, HOASlam(fn x: (int HOAS) HOAS => run x)which can also be written as follows in MetaML syntax: <fn x: <int> => ~(run <x>)>

11

First-Order Abstract Syntax

We can declare the following datatype in ML to represent untyped -expressions:

datatype exp = One | Shi of exp | Lam of exp | App of exp exp

For instance, the -expression x. y.y(x) is now represented as follows:

Lam (Lam (App(One, Shi (One))))

12

Typeful F.O.A.S.

FOAS: A binary type constructor(G,) FOAS: The type for the representation of an object-program of

type in which free program variables are assigned types by G, which is represented as a sequence of types.

FOASone: .. ( FOAS

FOASshi: .. ( FOAS FOAS

FOASlam: .. ( FOAS FOAS

FOASapp: .. FOAS FOAS FOAS

For instance, the simply typed l-expression x:int. y:int int.y(x)is represented as follows, FOASlam(FOASlam(FOASapp(FOASone, FOASshi(FOASone))))which can be assigned the type (,int (int int) int) FOAS

13

The Syntax for code

kinds ::= type | envtypes ::= | 1 2 | G, | type| env

type env. G| | Gconst. fun. cf ::= runconst. con. cc ::= Lift | One | Shi | Lam | App | Fixexpr. e ::= …values v ::= …expr. var. ctx. ::= | xtype. var. ctx. ::= | type | env

14

Code Constructors in code

We assign the following types to the code constructors

in code:

Lift: . ,

One: . ,

Shi: .. , ,

Lam: .. , ,

App: .. (,, , ) , Fix: . , ,

15

The function run in code

We can now assign the special function

run the following type:

run : , That is, only closed program is

allowedto be run!

16

A Staged Program in code

As an example, we can implement a staged powerfunction as follows:

fun mult (x: int) (y: int): int = x * yfun power (n: int) (x: <g, int>): <g, int> =

if n = 0 then Lift (1)else App (App (Lift mult, x), power (n-1) x)

We can then implement the square function asfollows:

val square: int int = run (Lam (power 2 One))

17

Meta-Programming Syntax

It is at least inconvenient, if not impractical, to construct meta-programs with abstract syntax trees.This situation is further worsened when de Bruijn indexes are used to represent program variables.To address the issue, we provide

meta-programming syntax to facilitate the construction of meta-programs

a translation that can “translate away” meta-programming syntax

18

Extending code to

Some additional syntax in :expr. e ::= … | `(e) | ^(e)expr. var. ctx. ::= | x@k

A typing judgment is of the form

; |k e : where G is a finite mapping from positive integersto type environments such that the domain of G isalways equal to the set {1,…,k}.

code

G

code

+

19

Some Typing Rules in

The typing rule for encoding:

The typing rule for decoding:

code

|k `(e) : <G(k+1;), >

G

| k+1 e :

G+G

|k e : <G(k+1;), >

G

| k+1 ^(e) :

G+G

20

Translating into code

(1)

Some generalized code constructors:Liftn : 1…n ..1,…,n ;

Lamn : 1…n ... 1,…, :: n ; 1,…,n ;

Appn : 1…n ... 1,…,n ; 1,…,n ; 1,…,n ;

Onen : 1…n ..1,…, :: n ;

Shin : 1…n ... 1,…,n ; 1,…,n ;

where we use 1,…,n ; for1,…n,

code

21

Translating into code

(2)

trans0(xs; x) = x if x@0 is in xs

trans0(xs; lam x.e) = lam x. trans0(xs,x@0; e)

trans0(xs; e1 (e2)) = (trans0(xs; e1))(trans0(xs; e2))

trans0(xs; `(e)) = trans1(xs; e)…

trans1(xs; x) = Lift (x) if x@0 is in xs

trans1(xs; x) = var1(xs, x) if x@1 is in xs

trans1(xs; lam x.e) = Lam x. trans1(xs,x@1; e)

trans1(xs; e1 (e2)) = App(trans1(xs; e1),trans1(xs; e2))

trans1(xs; `(e)) = trans2(xs; e)

trans1(xs; ^(e)) = trans0(xs; e)…

code

22

Translating into code

(3)

The main technical result of the paper:

Theorem Assume that ; e : is derivable. Then ; | trans(e) : is also derivable, where trans(e) = trans0(; e)

code

|0

23

Some Remarks

Bound variables at stage k>0 Let e = `(lam x. ^(f `x)) for some function f.

Then we have trans(e)=Lam (f One) f is the identity function lam x. x :

trans(e) reduces to Lam (One) f is the shifting function lam x. Shi (x) :

trans(e) reduces to Lam (Shi One) f is the lifting function lam x. Lift (x) :

trans(e) reduces to Lam (Lift One)

Cross-Stage Persistence (CSP) operator % % can be defined as ^Lift Lift can be defined as `%

24

Some Closely Related Work

MetalML (Sheard andTaha)Imperative MetaML (Calcagno et al )Environment Classifier (Taha and Nielsen)Staged Computation (Davis and Pfenning)Meta-Programming with Names and Necessities (Nanevski and Pfenning)Guarded Recursive Datatypes (Xi et al )

25

End of the Talk

Thank You! Questions?

top related