I'm learning macro by implementing simple list comprehension based on
Philip Wadler's transformation rules(the unoptimized ones for
simplicity). . The problem is, my program always returns the properly
converted s-expression instead of executing it. Below is my code. When
I tested it, I always got S-expression back. For example, if my test
case is (list-of x (x <- '(1 2 3 4)) (> x 2)), then I will get back
(map (lambda (x) (list-of x (> x 2))) '(1 2 3 4)), instead of the
result of *executing* the returned expression. I guess this is because
Scheme interprets that the quasiquoted expressions are returned result
instead of part of the transformation. Therefore, I also tried
removing quasiquotes, and simply using normal evaluation. However, by
doing that I couldn't get it work. Any suggestions? Thanks a bunch!
(define (generator? expr)
(eq? (cadr expr) '<-))
(define (gen-var expr)
(car expr))
(define (gen-list expr)
(caddr expr))
(define-syntax list-of
(syntax-rules ()
((_ e . rules)
(if (null? 'rules) e
(let ((rule (car 'rules))
(remaining (cdr 'rules)))
(cond
((not (generator? rule)) `(if ,rule (list-of
e ,@remaining)))
(else
(let ((var (gen-var rule))
(data (gen-list rule)))
`(map (lambda (,var) (list-of
e ,@remaining)) ,data)))))))))
The transformation rules I used are:
(a) TE[[E | v<- L: Q]] = map (lambda (v). TE[[E | Q]]) TE[L]
(b) TE[[E|B; Q]] = if TE[B] TE[[E|Q]] NIL
(c) TE[ [e |]] = Cons TE[E] NIL
> I'm learning macro by implementing simple list comprehension based on
> Philip Wadler's transformation rules(the unoptimized ones for
> simplicity).
You should mention the source where you got those rules from:
http://research.microsoft.com/~simonpj/papers/slpj-book-1987/index.htm
> The problem is, my program always returns the properly
> converted s-expression instead of executing it. Below is my code. When
> I tested it, I always got S-expression back. For example, if my test
> case is (list-of x (x <- '(1 2 3 4)) (> x 2)), then I will get back
> (map (lambda (x) (list-of x (> x 2))) '(1 2 3 4)), instead of the
> result of *executing* the returned expression. I guess this is because
> Scheme interprets that the quasiquoted expressions are returned result
> instead of part of the transformation.
Syntax-rules is not a computational macro-system, but rewrite rule
based (basically it does the thing you try to do, but automatic).
So with your macro:
(list-of e . rules)
is expanded into
(if (null? 'rules) e
(let ((rule (car 'rules))
(remaining (cdr 'rules)))
(cond
[...])))
which is then run and computes the s-expression. If you want an
computational macro system you should search for one of '(syntax-case
explicit-renaming-macros syntactic-closures define-macro). Mote that
these are not in the current scheme standard (but syntax-case might be
in the next). Common Lisps defmacro also falls into the class of
computational macro systems.
> Therefore, I also tried
> removing quasiquotes, and simply using normal evaluation. However, by
> doing that I couldn't get it work. Any suggestions? Thanks a bunch!
All the quoting and unquoting is allready done by syntax-rules in an
automated fashion, so you dont have to :)
You should also note that wadler does not use map but flat-map, which
is NOT the same as schemes map.
=== SPOILER ===
i have posted a version of list-of implemented with syntax-rules at:
http://paste.lisp.org/display/42060
(list-of (+ y 6) (x in '(1 2 3 4 5)) (odd? x) (y is (* x x))) => (7 15
31)
This will fail in odd ways:
(list-of (add1 y) (x in '(1 2 3 4 5)) (odd? x) (y is (* x x)))
. reference to undefined identifier: x
As the others write syntax-rules can't do arbitrary computations.
Despair not - syntax-case can. Here is a syntax-case version
written by turning
' into #'
` into #`
,@ into #,@
and then inserting syntax->list everywhere you car, cdr, null?
and friends to examine the syntax.
The snippet is *not* in good style, but it shows how to
use syntax-case in the "define-macro" style.
(begin-for-syntax
(define (generator? expr)
(eq? (cadr (syntax-object->datum expr)) '<-))
(define (gen-var expr)
(car (syntax->list expr)))
(define (gen-list expr)
(caddr (syntax->list expr))))
(define-syntax (list-of stx)
(syntax-case stx ()
((_ e . rules)
(let ([rules (syntax->list #'rules)])
(if (null? rules)
#'e
(let ((rule (car rules))
(remaining (cdr rules)))
(cond
((not (generator? rule))
#`(if #,rule (list-of e #,@remaining)))
(else
(let ((var (gen-var rule))
(data (gen-list rule)))
#`(map (lambda (#,var)
(list-of e #,@remaining)) #,data))))))))))
Welcome to DrScheme, version 370.2-svn29may2007 [3m].
Language: Pretty Big (includes MrEd and Advanced Student); memory limit:
900 megabytes.
> (list-of x
(x <- '(1 2 3 4))
(> x 2))
(#<void> #<void> 3 4)
--
Jens Axel Søgaard
Maybe someday I will reach macro nirvana. But not today. This one
works. Thanks, Joe.