This afternoon I implemented a simple partial evaluator for a Scheme-like language, based on the paper “Partial Evaluator as a Compiler for Reflective Languages” (Asai, Masuhara, Matsuoka and Yonezawa, 1995). So far I’ve restricted myself to an IO-free, side-effect free language, but by using the notion of preactions I ought to easily be able to add support for IO. I’d like to explore extending the simple system I have now to cope with the Composable Memory Transactions of Harris et al instead of supporting Scheme’s destructive updates.
The addition of pattern-matching data-destructuring will help the partial evaluator out, too, in the same way it makes dealing with primitive data easier for the Spineless Tagless G-Machine (see section 4.7). For example, using the partial evaluator to specialize the expression
(fold + 0 a)
I currently get
(letrec ((g17 (lambda (x acc) (if (null? x) acc (GOTO g17 (if (pair? x) (PRIMcdr x) (error '"Not a pair in cdr" x)) (+ (if (pair? x) (PRIMcar x) (error '"Not a pair in car" x)) acc)))))) (g17 a '0))
(Ignore the strange
GOTO syntax — I’m also
experimenting with identifying the places I can transform recursive
procedures into iterative loops.) Note that the type test
x) is repeated twice. If some kind of
were used instead, we would see the test lifted to wrap
(GOTO g17 ...) expression, leaving us with something
(letrec ((g17 (lambda (x acc) (if (null? x) acc (if (pair? x) (GOTO g17 (PRIMcdr x) (+ (PRIMcar x) acc)) (error '"Not a pair in match" x)))))) (g17 a '0))
which, when compiled, is about as efficient as you will get with a statically compiled Scheme. Lots of the optimizations developed in the Self system apply to dynamically compiled variants, of course, potentially leading to further efficiencies.