Is this really a compiler?
Yes, it is, though it’s hard to see it when we’re compiling TOY code directly to Racket closures.
Another way to see this in a more obvious way is to change the compiler code so instead of producing a Racket closure it spits out the Racket code that makes up these closures when evaluated.
The basic idea is to switch from a function that has code that “does stuff”, to a function that emits that code indtead. For example, consider a function that computes the average of two numbers
(/ (+ x y) 2))
to one that instead returns the actual code
(string-append "(/ (+ " x " " y ") 2)"))
It is, however, inconvenient to use strings to represent code: S-expressions are a much better fit for representing code:
(list '/ (list '+ x y) 2))
This is still tedious though, since the clutter of list
s and quotes
makes it hard to see the actual code that is produced. It would be nice
if we could quote the whole thing instead:
'(/ (+ x y) 2))
but that’s wrong since we don’t want to include the x
and y
symbols
in the result, but rather their values. Racket (and all other lisp
dialects) have a tool for that: quasiquote
. In code, you just use a
backquote `
instead of a '
, and then you can unquote parts of
the quasi-quoted code using ,
(which is called unquote
). (Later in
the course we’ll talk about these "`
"s and ",
"s more.)
So the above becomes:
`(/ (+ ,x ,y) 2))
Note that this would work fine if x
and y
are numbers, but they’re
now essentially arguments that hold expression values (as
S-expressions). For example, see what you get with:
Back to the compiler, we change the closure-generating compiler code
(define compiled-body (compile bound-body))
(define compiled-exprs (map compile exprs))
(lambda ([env : ENV])
(compiled-body (extend ...)))]
into
(define compiled-body (compile bound-body))
(define compiled-exprs (map compile exprs))
`(lambda ([env : ENV])
(,compiled-body (extend ...)))]
Doing this systematically would result in something that is more clearly
a compiler: the result of compile
would be an S-expression that you
can then paste in the Racket file to run it.
An example of this idea taken seriously is the graal + truffle combination for implementing fast JIT compiled languages: