118 <strong>Scheme</strong> <strong>and</strong> <strong>Functional</strong> <strong>Programming</strong>, <strong>2006</strong>
A Self-Hosting Evaluator using HOAS A <strong>Scheme</strong> Pearl Eli Barzilay Northeastern <strong>University</strong> eli@barzilay.org Abstract We demonstrate a tiny, yet non-trivial evaluator that is powerful enough to run practical code, including itself. This is made possible using a Higher-Order Abstract Syntax (HOAS) representation — a technique that has become popular in syntax-related research during the past decade. With a HOAS encoding, we use functions to encode binders in syntax values, leading to an advantages <strong>of</strong> reflecting binders rather than re-implementing them. In <strong>Scheme</strong>, hygienic macros cover problems that are associated with binders in an elegant way, but only when extending the language, i.e., when we work at the meta-level. In contrast, HOAS is a useful object-level technique, used when we need to represent syntax values that contain bindings — <strong>and</strong> this is achieved in a way that is simple, robust, <strong>and</strong> efficient. We gradually develop the code, explaining the technique <strong>and</strong> its benefits, while playing with the evaluator. 1. Introduction Higher-Order Abstract Syntax (HOAS) is a technique for representing syntax with bindings using functions. This is a form <strong>of</strong> reflection in the sense that binders in the object level are represented using binders in the meta level. The result is simple (no need for sophisticated substitution mechanisms), robust (the meta-level is our language, which better implement scope correctly), <strong>and</strong> efficient (as it is part <strong>of</strong> the core implementation). HOAS has been in use for a while now [13], a good overview is given by H<strong>of</strong>mann [9], <strong>and</strong> in [1, Section 4.7]. However, it is more popular in the strictly typed world than it is in <strong>Scheme</strong>. In part, this may be due to <strong>Scheme</strong>’s hygienic macro facility, which allows hooking new kinds <strong>of</strong> syntactic constructs into the language in a way that respects lexical scope. Using a high level macro system means that <strong>Scheme</strong>rs rarely need to represent syntax directly — instead, they work at the meta-level, extending the language itself. This is unfortunate, as HOAS can be a useful tool for syntax-related work. Furthermore, it is possible to formalize HOAS in a way that is more natural in a dynamically-typed language than it is in statically-typed ones, which corresponds to a HOAS formalization in Nuprl [3] that builds on the predicative nature <strong>of</strong> its type theory [2, 1, 12]. The purpose <strong>of</strong> this <strong>Scheme</strong> pearl is to demonstrate the use <strong>of</strong> HOAS in <strong>Scheme</strong>, with the goal <strong>of</strong> making this technique more accessible to <strong>Scheme</strong>rs 1 . As demonstrated below, using macros in <strong>Scheme</strong> facilitates the use <strong>of</strong> HOAS, as there is no need for an external tool for translating concrete syntax into HOAS representations. In itself, HOAS is a representation tool for object-level values, not for meta-level work where bindings are directly accessible in some way. It is, however, used in some meta-linguistic systems for implementing syntactic tools 2 . For example, it could be used as the underlying term representation in a language-manipulating tool like PLT’s Reduction Semantics [11]. 2. A Toy Evaluator The presentation begins with a simple evaluator. Our goal is to evaluate a Lambda-Calculus-like language using reductions, so we need a representation for lambda abstractions <strong>and</strong> applications. To make this example more practical, we also throw in a conditional if special form, make it h<strong>and</strong>le multiple arguments, <strong>and</strong> use callby-value. A common evaluator sketch for such a language is 3 : (define (ev expr) (cond [(not (pair? expr)) expr] [(eq? ’if (car expr)) (ev (if (ev (cadr expr)) (caddr expr) (cadddr expr)))] [else (ev (let ([f (ev (car expr))]) (substitute (body-<strong>of</strong> f) (args-<strong>of</strong> f) (map ev (cdr expr)))))])) where an application is always assumed to have an abstraction in its head position, <strong>and</strong> the args-<strong>of</strong> <strong>and</strong> body-<strong>of</strong> functions pull out the corresponding parts. As expected, the main issue here is implementing a proper substitution function. Common approaches include using symbols <strong>and</strong> renaming when needed, or using symbols ‘enriched’ with lexical binding information (‘colors’). Proceedings <strong>of</strong> the <strong>2006</strong> <strong>Scheme</strong> <strong>and</strong> <strong>Functional</strong> <strong>Programming</strong> Workshop <strong>University</strong> <strong>of</strong> Chicago Technical Report TR-<strong>2006</strong>-06 1 The presentation is loosely based on a comp.lang.scheme post from October 2002. 2 It might be possible that HOAS can be used for implementing a lowlevel macro facility that the high-level hygienic macro system builds on. HOAS should not be confused with current low-level syntactic systems like syntactic closures or syntax-case. 3 Note that details like error checking are omitted, <strong>and</strong> that we use atomic <strong>Scheme</strong> values such as booleans <strong>and</strong> numbers to represent themselves. 119
- Page 1:
Scheme and Functional Programming 2
- Page 4 and 5:
4 Scheme and Functional Programming
- Page 6 and 7:
6 Scheme and Functional Programming
- Page 8 and 9:
• A web browser that plays the ro
- Page 10 and 11:
and requests. When a client request
- Page 12 and 13:
above prevents pages from these dom
- Page 14 and 15:
14 Scheme and Functional Programmin
- Page 16 and 17:
2. Explaining Macros Macro expansio
- Page 18 and 19:
For completeness, here is the macro
- Page 20 and 21:
onment is extended in the original
- Page 22 and 23:
expand-term(term, env, phase) = emi
- Page 24 and 25:
Derivation ::= (make-mrule Syntax S
- Page 26 and 27:
True derivation (before macro hidin
- Page 28 and 29:
We assume that the reader has basic
- Page 30 and 31:
the value of the %eax register by 4
- Page 32 and 33:
Code generation for the new forms i
- Page 34 and 35:
we introduced in 3.11. The only dif
- Page 36 and 37:
the user code from interfering with
- Page 38 and 39:
38 Scheme and Functional Programmin
- Page 40 and 41:
mization and on creating efficient
- Page 42 and 43:
(a) stage (b) fifo (c) split (d) me
- Page 44 and 45:
This tagging is used later in the c
- Page 46 and 47:
(let ((clo_25 (%closure (lambda (y)
- Page 48 and 49:
nb. cycles per element 1400 1200 10
- Page 50 and 51:
50 Scheme and Functional Programmin
- Page 52 and 53:
a ∗ ❅ left right · b ❅ a S
- Page 54 and 55:
T ([spec], w) = { {w}, if w ∈ L([
- Page 56 and 57:
A([spec]): ✓✏ (where L([spec])
- Page 58 and 59:
construction commands. It is possib
- Page 60 and 61:
; Regular expression for Scheme num
- Page 62 and 63:
References [1] A. V. Aho, R. Sethi,
- Page 64 and 65:
(let ((n (cond ((char? var0) ) ((sy
- Page 66 and 67:
3. Survey An incomplete survey of a
- Page 68 and 69: case monster 10 literals 100 litera
- Page 70 and 71: 70 Scheme and Functional Programmin
- Page 72 and 73: modest programming requirements, an
- Page 74 and 75: development of incomplete subsystem
- Page 76 and 77: the previous request. This method a
- Page 78 and 79: could be run indefinitely. This fun
- Page 80 and 81: programmers reject Scheme without r
- Page 82 and 83: (define interp (λ (env e) (case e
- Page 84 and 85: Before describing the run-time sema
- Page 86 and 87: Figure 5. Cast Insertion Figure 6.
- Page 88 and 89: Figure 7. Evaluation Figure 8. Eval
- Page 90 and 91: catching type errors, as we do here
- Page 92 and 93: [34] J. C. Reynolds. Types, abstrac
- Page 94 and 95: let id (T:*) (x:T) : T = x; The tra
- Page 96 and 97: Figure 3: Regular Expressions and N
- Page 98 and 99: Figure 5: Evaluation Rules Evaluati
- Page 100 and 101: 5. Exact Substitution: E, (x = v :
- Page 102 and 103: Figure 9: Subtyping Algorithm Algor
- Page 104 and 105: for most type variables, and that m
- Page 106 and 107: 2. miniKanren Overview This section
- Page 108 and 109: 3. Pseudo-Variadic Relations Just a
- Page 110 and 111: Replacing run 10 with run ∗ cause
- Page 112 and 113: As might be expected, we could use
- Page 114 and 115: Of course there are still infinitel
- Page 116 and 117: To ensure that streams produced by
- Page 120 and 121: On the other hand, we can use a hig
- Page 122 and 123: (ev* (Q (lambda (x) (+ x 1)))) # >
- Page 124 and 125: (term ’lam (lambda (x) (if (equal
- Page 126 and 127: a message: there is no guarantee th
- Page 128 and 129: (! (self) 3) (?) =⇒ 1 (?? odd?) =
- Page 130 and 131: (define new-server (spawn (lambda (
- Page 132 and 133: The abstraction shown in this secti
- Page 134 and 135: Erlang Termite List length (µs) (
- Page 136 and 137: 136 Scheme and Functional Programmi
- Page 138 and 139: page of j contains the URL of the p
- Page 140 and 141: 4. Solution The failed attempts abo
- Page 142 and 143: · ; · ; · :: Store × Frame Stac
- Page 144 and 145: logically creates a sub-session of
- Page 146 and 147: on this work, including Ryan Culpep
- Page 148 and 149: ing application operation. 1 As a r
- Page 150 and 151: such as image glyphs corresponding
- Page 152 and 153: Phone For clarity, this code pr
- Page 154 and 155: A. Porting TinyScheme to Qualcomm B
- Page 156 and 157: 156 Scheme and Functional Programmi
- Page 158 and 159: ware installers have to install any
- Page 160 and 161: defines a module named circle-lib i
- Page 162 and 163: Figure 2. Sometimes special cases a
- Page 164 and 165: Considering all of these issues tog