> I am currently working on creating my own language in racket. Therefore I
> would like to change the standard lambda implementation to a version in which
> a certain method call is interleaved between every 2 execution lines of the
> body.
The macro system in Racket allows us to express a compile-time
computation. In particular, during compilation, Racket walks through
our programs and applies "macro expansion" to transform our programs
down to simple core forms.
To interleave a particular statement between pairs of body elements,
we might imagine that the macro takes the body of a function:
b_1 b_2 ... b_n
and does some funny injection between each b_i. Here's what this
might look like:
b_1 (printf "hi\n") b_2 (printf "hi\n") ... b_n)
;; interleaving.rkt ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#lang racket
(require (for-syntax racket/list))
(define-syntax (define/interleaved stx)
(syntax-case stx ()
[(_ (fun-name ids ...) body ...)
(begin
(define body-element-syntaxes (syntax->list #'(body ...)))
(define interleaved-body-element-syntaxes
(add-between body-element-syntaxes #'(printf "hi\n")))
#`(define (fun-name ids ...)
#,@interleaved-body-element-syntaxes))]))
;; Example:
(define/interleaved (f x)
(printf "this is a test\n")
(* x x))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
When we compile this program, Racket sees the use of
"(define/interleaved ...)". It then uses the macro associated to that
name to transform the source program, at compile-time.
define/interleaved is a function that takes the literal text of the
program and destructures it and restructures it to a new, simpler
program. In the example above, the new program is in the form of a
syntax object:
#`(define (fun-name ids ...)
#,@interleaved-body-element-syntaxes))
It uses portions of the old syntax tree, like the original function's
name and argument list. But it also carries along an amended list of
body elements that we computed at compile-time.
>From that point forward, macro expansion continues. Note that there's
no runtime evaluation of the program itself during compilation: the
compile-time phase is distinct from the run-time phase of a program.
One thing to note is that the macro system recognizes the use of
macros by name. But since the Racket language already provides a
"(define ...)" like form, we may want to hide it away if we're
defining our own language. This is where the Racket "module" system
kicks in: modules allow us to carve out a language that provides just
our special forms. That is, to "change" the existing definition of a
form is really to mask it away.
Let's say that we add to 'interleaving.rkt' the following:
(provide (except-out (all-from-out racket)
define))
(provide (rename-out (define/interleaved define)))
Then we can treat 'interleaving.rkt' as a language that acts just like
regular racket, except in the special case where we use define. We
can write a new program in the same directory with the following
content:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#lang s-exp "interleaving.rkt"
(define (f x)
(displayln "hello")
(displayln "world"))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
And suddenly it looks as though "define" has been changed, and that's
because the use of define in this test program is really a use of
'define/interleaved': we've used the module system to reroute uses of
'define' to use our definition instead of the base one.
____________________
Racket Users list:
http://lists.racket-lang.org/users