(define-syntax my-rev
(lambda (x)
(syntax-case x ()
((_ e1 e2 ...)
(syntax (reverse '(e1 e2 ...)))))))
(my-rev 1 2 +)
I've also tried it without quoting (e1 e2 ...) but that
results in an "error: call of non-procedure: 1"
message. Is there a simple introduction to
hygienic macros? I've tried both chapter 8 of "the
scheme programming language" and also Dybvig's
paper on Writing Hygienic Macros in Scheme with
syntax-case but they are both a little too advanced
for me at the moment.
Ian
Here is one solution in PLT Scheme:
(define-syntax (my-rev stx)
(syntax-case stx ()
((_ e ... op)
#`(op #,@(reverse (syntax->list #'(e ...)))))))
(my-rev 1 2 +)
--
Jens Axel Søgaard
Here's another:
(define-syntax my-rev
(lambda (stx)
(with-syntax (((e0 ...) (reverse (cdr (syntax->list stx)))))
(syntax (e0 ...)))))
Hi guys,
Thanks very much - this worked great.
Cheers,
Ian
And here's the portable R5RS version:
(define-syntax my-rev
(syntax-rules ()
((_ . args)
(letrec-syntax
((rev (syntax-rules ()
((_ x) x)
((_ x e1 . e2) (rev (e1 . x) . e2)))))
(rev () . args)))))
--
Alex
And here is the portable R6RS version:
(define-syntax (my-rev x)
(syntax-case x ()
[(_ a* ...) (reverse #'(a* ...))])))
Aziz,,,
Is it me, or is the old defmacro more expressive?
(defmacro reversing-arguments (fun &rest args) `(,fun ,(reverse args)))
(reversing-arguments (- 1 2)) --> 1
--
__Pascal Bourguignon__ http://www.informatimago.com/
You never feed me.
Perhaps I'll sleep on your face.
That will sure show you.
Than R5RS macros? Of course.
--
Jens Axel Søgaard
Without significant experience of either it seems more intuitive
at least. I've been looking at docs and papers on hygienic macros
for days now and I'm still not getting it :(
Ian
> And here is the portable R6RS version:
>
> (define-syntax (my-rev x)
> (syntax-case x ()
> [(_ a* ...) (reverse #'(a* ...))])))
This one ought to work too?
(define-syntax (my-rev stx)
(reverse (cdr (syntax->list stx)))
Ian: Here is a work through of the expansion
of (my-rec 1 2 +). First a syntax-object representing
(my-rec 1 2 +). Think of the syntax-object as the
list '(my-rec 1 2 +) enriched with extra information.
R6RS and others use #' as shortcut for (syntax ...),
so I'll write it as #'(my-rec 1 2 +).
Now (syntax->list #'(my-rev 1 2 +) evaluates to
a list of syntax-objects. The first syntax-object
represents the my-rev, the second the 1 and so on.
(syntax->list #'(my-rev 1 2 +)
-> (#'my-rev #'1 #'2 #'+)
Now cdr is taken to get rid of the my-rev:
(cdr (#'my-rev #'1 #'2 #'+))
-> (#'1 #'2 #'+)
Now reverse is called on the list
(reverse (#'1 #'2 #'+))
=> (#'+ #'2 #'1)
This list is now returned as the result of expansion.
In PLT Scheme the result of a macro expansion must
be a syntax-object, so to run it in PLT Scheme
we must convert the list to a syntax-object. A
clever shortcut is #`#, .
(define-syntax (my-rev stx)
#`#,(reverse (cdr (syntax->list stx)))
but one can also use datum->syntax-object which
takes two arguments. The first a syntax-object with
source location info (among other things) and the
datum (in this case the list) to convert to a
syntax-object.
(define-syntax (my-rev stx)
(datum->syntax-object stx
(reverse (cdr (syntax->list stx)))))
Morale: If you really want to program in a defmacro style, nothing
stops you. Just convert everything to lists and do as
you normally would. Before returning the result convert
to a syntax-object.
--
Jens Axel Søgaard
Very good!
Thank you,
--
__Pascal Bourguignon__ http://www.informatimago.com/
"I have challenged the entire quality assurance team to a Bat-Leth
contest. They will not concern us again."
[snip]
Hi Jens,
This is a great explanation - thanks. I guess this isn't particularly
idiomatic scheme though?
I've started looking at "Practical Common Lisp" and I'm trying to
convert the macro that generates the nifty where function.
What I've got so far is the following:
(define-syntax my-where-helper
(lambda (stx)
(syntax-case stx ()
((_) (syntax #t))
((_ e1 e2 e3 ...)
(syntax (and (let ((kw (get-keyword e1 '(#:abc "abc" #:xyz
"xyz"))))
(if kw (string=? kw e2) #t))
(my-where-helper e3 ...)))))))
(define-syntax my-where
(lambda (orig-x)
(syntax-case orig-x ()
((_ c0 c1 c2 ...)
(with-syntax ((tail (syntax-case (syntax (c0 c1 c2 ...)) ()
(() (syntax ()))
((e1 e2 e3 ...)
;; (syntax (and (let ((kw (get-keyword
e1 cd)))
(syntax (and (let ((kw (get-keyword e1
'(#:abc "abc" #:xyz "xyz"))))
(if kw (string=? kw e2)
#t))
;; (my-where e3 ...))))
(my-where-helper e3
...))))
((_) (syntax-error orig-x)))))
(syntax-case (syntax c0) ()
(_ (syntax (lambda (cd) tail)))))))))
Apart from the obvious flaw that the cd is hardcoded rather than passed
in as the lambda parameter it has a few other issues. I couldn't get
it to work without the helper macro. And I assume it should use
datum->syntax-object somehow to use the external cd rather than
transforming it?
I'm using chicken scheme rather than PLT scheme so some of the
syntax you use doesn't work for me.
Ian
Yes, except that in your example, syntax->list may fail. Using the
pattern-language makes the code more robust and more readable (once you
get used to reading patterns I guess).
Aziz,,,
Ian, this is a good point.
The pattern matching version checks whether stx is representing
a list, so reverse will never be called with a non-list. Instead
a syntax error will be signaled with the location of offending syntax.
The short version will on the other hand call reverse on a non-list,
and thus the error location will point to the call to reverse and
not the faulty syntax.
An example of a faulty use of my-rev is:
(my-rev 1 2 . +)
So if we want to extend the "defmacro-style" macro to do the same
error checking as the syntax-case version, we need to write:
(define-syntax (my-rev1 stx)
(let ([l (syntax->list stx)])
(if (list? l)
#`#,(reverse (cdr l))
(raise-syntax-error 'my-rev1 "bad syntax" stx))))
The last argument of raise-syntax-error is a syntax-object,
whose source location information is used by the system to
report the location of the error. If we give raise-syntax-error
the original syntax-object the entire macro call will be
highlighted.
The morale is thus: The syntax-case system makes helps
the programmer to write robust macros with precise
error locations. It is possible, but cumbersome, to do
the same without pattern matching.
--
Jens Axel Søgaard
> So if we want to extend the "defmacro-style" macro to do the same
> error checking as the syntax-case version, we need to write:
>
> (define-syntax (my-rev1 stx)
> (let ([l (syntax->list stx)])
> (if (list? l)
> #`#,(reverse (cdr l))
> (raise-syntax-error 'my-rev1 "bad syntax" stx))))
syntax->list fails again for the same reason. It wasn't reverse that I
was worried about.
Is the morale of the story clear? ;-)
Aziz,,,
Oops. I thought syntax->list would signal an error in mzscheme when
given a syntax-object that does not represent a list; apparently it
doesn't (instead it returns a non-list even though it's name *is*
syntax->*list* !!):
> (syntax->list #'(1 2 . 3))
#f
> (syntax->list #'(1 2 3))
(.#<syntax:8:19> .#<syntax:8:21> .#<syntax:8:23>)
>
So, for this implementation of syntax->list, you're right to worry
about reverse.
Aziz,,,