10 September Implementing Staged Computation Chiyan Chen and Hongwei Xi Boston University
10 September Talk Overview Staged Computation Guarded Recursive Datatype Constructors Code Representation – Higher-order Abstract Syntax – First-order Abstract Syntax (via de Bruijn indices)
10 September An Example 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))))
10 September An Example in MetaML (* ‘run’ is a built-in function of the type ‘a *) fun power (n: int) (x: ): = if n = 0 then else val square = run ~(power 2 )> val cube = run ~(power 3 )>
10 September Scheme vs. MetaML Most significantly, there is a type system in MetaML but none in Scheme for verifying whether staging is done correctly. However, ‘code’ can be readily represented in Scheme but not in ML
10 September Scheme vs. MetaML (continued) 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))))
10 September G.R. Datatype Constructors A simple and natural generalization of the notion of datatypes in ML – We use for a guarded type, where may contain some constraints on type variables in – A g.r. datatype constructor is used to construct g.r. datatypes, that is, sums of guarded types.
10 September A Representation for Types The g.r. datatype constructor TY is for representing simple types: typecon (type) TY = (int) TYint | {’a,’b}. (’a * ’b) TYtup of ’a TY * ’b TY | {’a,’b}. (’a ’b) TYfun of ’a TY * ’b TY | {’a}. (’a TY) TYtyp of ’a TY TYint: (int) TY TYtup: {’a,’b}. ’a TY * ’b TY (’a * ’b) TY TYfun: {’a,’b}. ’a TY * ’b TY (’a ’b) TY TYtyp: {’a}. ’a TY (’a TY) TY
10 September The Representation of Some Types The type ‘int’ is represented as TYint The type ‘int * int’ is represented as TYtup(TYint, TYint) The type ‘int int * int’ is represented as TYfun(TYint, TYtup (TYint, TYint)) The type ‘(int) TY’ is represented as TYtyp(TYint)
10 September A Program Example: val2string fun val2string pf x = case pf of TYint => int2string x | TYtup (pf1, pf2) => “(” ^ val2string pf1 (fst x) ^ “,” ^ val2string pf2 (snd x) ^ “)” | TYfun _ => “[a function]” | TYtyp _ => “[a type]” withtype {’a}. ’a TY -> ’a -> string
10 September H.O.A.S. Trees typecon (type) HOAS = {’a}. (’a) HOASlift of ’a | {’a}. (’a) HOASif of bool HOAS * ’a HOAS * ’a HOAS | {’a,’b}. (’a * ’b) HOAStup of ’a HOAS * ’b HOAS | {’a,’b}. (’a ’b) HOASlam of ’a HOAS ’b HOAS | {‘a,’b}. (‘b) HOASapp of (‘a ’b) HOAS * ‘a HOAS | {’a}. (’a) HOASfix of ’a HOAS ’a HOAS HOASlift: {’a}. ’a ’a HOAS HOASif: {’a}. bool HOAS * ’a HOAS * ’a HOAS ’a HOAS HOAStup: {’a,’b}. ’a HOAS * ’b HOAS (’a * ’b) HOAS HOASlam: {’a,’b}. (’a HOAS ’b HOAS) (’a ’b) HOAS HOASapp: {‘a,’b}. (‘a ‘b) HOAS * ‘a HOAS ‘b HOAS HOASfix: {’a}. (’a HOAS ’a HOAS) ’a HOAS
10 September A Type-Preserving Evaluator fun eval (HOASlift v) = v | eval (HOASif (b, e1, e2)) = if eval (b) then eval (e1) else eval (e2) | eval (HOAStup (e1, e2)) = (eval (e1), eval (e2)) | eval (HOASlam (f)) = fn x => eval (f (HOASlift x)) | eval (HOASapp (e1,e2)) = (eval e1) (eval e2) | eval (HOASfix f) = eval (f (HOASfix f)) withtype {’a}. ’a HOAS ’a
10 September Compiling H.O.A.S. Trees We need a compilation function that can compile h.o.a.s trees: run: {’a}. ’a HOAS ’a (think about normalization by evaluation) For instance, this can be done in such a manner …
10 September A Staged Program fun power1 n = HOASlam ( fn x: int HOAS => if n = 0 then HOASlift 1 else HOASapp ( HOASlift op , HOAStup (x, HOASapp (power1 n-1, x)))) withtype int (int int) HOAS val square1 = run (power1 2) The function square1 is like being defined as: fun square1 x = x f1 (x) and f1(x) = x f0(x) and f0(x) = 1
10 September Another Staged Program fun power2 n x = if n = 0 then HOASlift 1 else HOASapp (HOASlift op , HOAStup (x, power2 (n-1) x) withtype int int HOAS int HOAS val square2 = run (HOASlam (power2 2)) The function square2 is like being defined as fun square2 x = x (x 1)
10 September Syntactic Sugar = ( ) HOAS `(x) = HOASlift (x) `(c) = HOASlift (c) `(e1, e2) = HOAStup (`(e1), `(e2)) `(e1(e2)) = HOASapp(`(e1), `(e2)) `(fn x => e) = HOASlam (fn x => `(e[x -> ^x])) `(fix f => e) = HOASfix (fn f => `(e[f -> ^f])) `(e: ) = (`(e): ( ) HOAS) `(^(e)) = e
10 September An Example in Sugared Syntax fun power1 n = `(fn x => ^(if n = 0 then lift (1) else `(x ^(power1 (n-1)) x))) withtype int val square1: int int = run (power1 2)
10 September Another Example in Sugared Syntax fun power2 n x = if n = 0 then lift (1) else `(^x ^(power2 (n-1) x)) withtype int val square2: int int = run `(fn x => ^(power2 2 `(x))
10 September The Usual Ackermann Function fun acker m n = if m =0 then n+1 else acker (m-1) (if n = 0 then 1 else acker m (n-1)) withtype int int int
10 September A Staged Ackermann Function The Ackermann function can be staged as follows: fun acker m = `(fix f => fn n => ^(if m = 0 then `(n+1) else `(^(acker (m-1)) (if n = 0 then 1 else f (n-1))))) withtype int
10 September A Staged Ackermann Function (contd.) For instance, we can define a function acker2 as follows: fun acker2 = run (acker 2) This definition is similar to the following one: fun acker2 n = acker1 (if n = 0 then 1 else acker2 (n-1)) and acker1 n = acker0 (if n = 0 then 1 else acker1 (n-1)) and acker0 n = n+1
10 September A Problem with H.O.A.S. Trees The expression e = `(fn x => ^(run `(x))), whose translation is HOASlam (fn x => run x), can be assigned the type: {‘a}. (‘a HOAS ‘a) HOAS However, ‘run e’ causes a run-time error
10 September F.O.A.S. Trees typecon (type,type) FOAS = | {’a,’g}. (’a,’g) FOASlift of ’a | {’a,’g}. (‘a,’a*’g) FOASone | {’a,’b,’g}. (‘a,’b*’g) FOASshift of (‘a,’g) FOAS | {‘a,’b,’g}. (‘a -> ‘b,’g) FOASlam of (‘b,’a*’g) FOAS | … FOASlift: {’a,’g}. ’a -> (’a,’g) FOAS FOASone: {‘a,’g}. (‘a,’a*’g) FOAS FOASshift: {‘a,’b’,’g}. (‘a,’g) FOAS -> (‘a,’b*’g) FOAS FOASlam: {‘a,’b,’g}. (‘b,’a * ‘g) FOAS -> (‘a -> ‘b,’g) FOAS …
10 September Some Examples of F.O.A.S. Trees The F.O.A.S. tree for ‘fn x => x’: FOASlam (FOASone) The F.O.A.S. tree for ‘fn x => fn y => x+y’: FOASlam(FOASlam( FOASapp (FOASlift +, FOAStup (FOASshift (FOASone), FOASone))))
10 September Some Examples of F.O.A.S. Trees FOASone can be assigned the type: (int, int * unit) FOAS FOASapp (FOASone, FOASshift(FOASone)) can be assigned the type: (int, (int int) * (int * unit)) FOAS FOASapp (FOASshift(FOASone), FOASone) can be assigned the type: (int, int * ((int int) * unit)) FOAS
10 September Compiling F.O.A.S trees We need a function to compile closed F.O.A.S. trees: run: {‘a}. (‘a, unit) FOAS ‘a This can be readily implemented …
10 September A Simple Example fun power1 n = if n = 0 then FOASlam (FOASlift 1) else FOASlam ( FOASapp ( FOASlift *, FOAStup (FOASone, FOASapp (power1 (n-1), FOASone)))) withtype {‘g}. int (int int, ‘g) FOAS val square = run (power1 2)
10 September Another Simple Example fun power2 n x = if n = 0 then FOASlift (1) else FOASapp (FOASlift *, FOAStup (x, power2 (n-1) x)) withtype {‘g}. int (int, ‘g) FOAS (int, ‘g) FOAS val square = run (FOASlam (power2 2 FOASone))
10 September Conclusion We have presented two approaches (h.o.a.s. and f.o.a.s.) to representing ‘code’ through the use of g.r. datatype constructors With such concrete represention of code, we can implement staged computation by simply translating away staging notations in staged programs
10 September Related Work Guarded Recursive Datatype Constructors (Xi et al) MetalML (Sheard et al) Staged Computation (Pfenning and Davis) … …
10 September End of the Talk Thank you! Questions?