Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How do I provide a macro that reverses params before execution?

3 views
Skip to first unread message

ove...@hotmail.com

unread,
Sep 5, 2006, 10:14:47 AM9/5/06
to
I tried the following but instead of 3 I got (+ 1 2).

(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

Jens Axel Søgaard

unread,
Sep 5, 2006, 10:24:11 AM9/5/06
to
ove...@hotmail.com skrev:

> I tried the following but instead of 3 I got (+ 1 2).
>
> (define-syntax my-rev
> (lambda (x)
> (syntax-case x ()
> ((_ e1 e2 ...)
> (syntax (reverse '(e1 e2 ...)))))))
>
> (my-rev 1 2 +)

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

Anton van Straaten

unread,
Sep 5, 2006, 10:36:01 AM9/5/06
to

Here's another:

(define-syntax my-rev
(lambda (stx)
(with-syntax (((e0 ...) (reverse (cdr (syntax->list stx)))))
(syntax (e0 ...)))))

ove...@hotmail.com

unread,
Sep 5, 2006, 3:25:51 PM9/5/06
to
Anton van Straaten wrote:

> Jens Axel Søgaard wrote:
> > 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 +)
>
> 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

Alex Shinn

unread,
Sep 6, 2006, 11:45:48 PM9/6/06
to
Anton van Straaten wrote:
> Here's another:
>
> (define-syntax my-rev
> (lambda (stx)
> (with-syntax (((e0 ...) (reverse (cdr (syntax->list stx)))))
> (syntax (e0 ...)))))

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

Abdulaziz Ghuloum

unread,
Sep 7, 2006, 2:24:40 AM9/7/06
to

And here is the portable R6RS version:

(define-syntax (my-rev x)
(syntax-case x ()
[(_ a* ...) (reverse #'(a* ...))])))

Aziz,,,

Pascal Bourguignon

unread,
Sep 7, 2006, 3:06:42 PM9/7/06
to
"Alex Shinn" <alex...@gmail.com> writes:

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.

Jens Axel Søgaard

unread,
Sep 7, 2006, 3:25:37 PM9/7/06
to
Pascal Bourguignon skrev:

> "Alex Shinn" <alex...@gmail.com> writes:
>
>> Anton van Straaten wrote:
>>> Here's another:
>>>
>>> (define-syntax my-rev
>>> (lambda (stx)
>>> (with-syntax (((e0 ...) (reverse (cdr (syntax->list stx)))))
>>> (syntax (e0 ...)))))
>> 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)))))
>
> Is it me, or is the old defmacro more expressive?

Than R5RS macros? Of course.

--
Jens Axel Søgaard

ove...@hotmail.com

unread,
Sep 7, 2006, 5:53:27 PM9/7/06
to
Pascal Bourguignon wrote:

> "Alex Shinn" <alex...@gmail.com> writes:
> > 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)))))
>
> Is it me, or is the old defmacro more expressive?
>
> (defmacro reversing-arguments (fun &rest args) `(,fun ,(reverse args)))
>
> (reversing-arguments (- 1 2)) --> 1

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

Jens Axel Søgaard

unread,
Sep 7, 2006, 6:32:37 PM9/7/06
to
Abdulaziz Ghuloum skrev:

> 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

Pascal Bourguignon

unread,
Sep 7, 2006, 7:32:12 PM9/7/06
to
Jens Axel Søgaard <use...@soegaard.net> writes:
> 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.

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."

ove...@hotmail.com

unread,
Sep 8, 2006, 5:10:17 AM9/8/06
to
Jens Axel Søgaard wrote:
> 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 +).

[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

Abdulaziz Ghuloum

unread,
Sep 8, 2006, 7:30:24 AM9/8/06
to

Jens Axel Søgaard wrote:
> Abdulaziz Ghuloum skrev:
>
> > 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)))

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,,,

Jens Axel Søgaard

unread,
Sep 8, 2006, 8:29:58 AM9/8/06
to
Abdulaziz Ghuloum skrev:

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

Abdulaziz Ghuloum

unread,
Sep 8, 2006, 6:33:26 PM9/8/06
to

Jens Axel Søgaard wrote:

> 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,,,

Abdulaziz Ghuloum

unread,
Sep 8, 2006, 7:04:40 PM9/8/06
to

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,,,

0 new messages