View
223
Download
0
Embed Size (px)
Citation preview
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?