From: rif
Subject: getting commas into a macro-defining-macro expansion
Date: 
Message-ID: <wj0zn7v2k1c.fsf@five-percent-nation.mit.edu>
I'm trying to write a macro-defining-macro in order to put a nice
facade on top of my CL-to-R gateway.  In particular, I'd like to be
able to say
 
(def-r-call r-plot (plot sequence)
  (xlab "") ylab)
 
and have this expand into the following macro definition:
 
(defmacro r-plot (sequence &rest rest &key (xlab "") ylab &allow-other-keys)
  `(r plot ,sequence :xlab ,xlab :ylab ,ylab
                     ,@(remove-plist rest :xlab :ylab)))
 
I'm pretty close, but I can't quite seem to get it.  Right now, I have
  
(defmacro def-r-call ((macro-name r-name &rest required-args)
                      &rest keyword-args)
  (let* ((rest-sym (gensym "rest"))
         (keyword-names (mapcar #'atom-or-first keyword-args))
         (keywords (mapcar #'to-keyword keyword-names)))
    `(defmacro ,macro-name (,@required-args
                            &rest ,rest-sym
                            &key ,@keyword-args)
       `(r ,',r-name
           ,@',required-args
           ,@',(mapcan #'(lambda (k n) (list k n))
                       keywords
                       keyword-names)
           (remove-plist ,,rest-sym ,@',keywords)))))


the auxiliary functions are all given at the end of this post
(remove-plist was cribbed from clocc, props to Sam Steingold).
 
The expansion is close to what I want, but clearly not right:
 
CL-USER> (macroexpand-1 '(def-r-call (r-plot plot sequence) (xlab "") ylab))
(DEFMACRO R-PLOT (SEQUENCE &REST #:|rest3667| &KEY (XLAB "") YLAB)
  `(R PLOT
      SEQUENCE
      :XLAB
      XLAB
      :YLAB
      YLAB
      (REMOVE-PLIST ,#:|rest3667| :XLAB :YLAB)))
T
 
The problem I have is getting a list (where the list is bound at
compile-time) to expand into a list of arguments preceded by a comma.
So I don't know how to get ,sequence instead of sequence in the expansion,
and similarly I need
 
:xlab ,xlab :ylab ,ylab
 
instead of
 
:xlab xlab :ylab ylab
 
Any ideas on how to achieve this are appreciated.  My knowledge of
nested backquote is sketchy at best --- most of what works above is
from experimentation rather than deep understanding.
 
Cheers,
 
rif

Auxiliary functions used by the def-r-call macro:

(defun atom-or-first (val)
  (if (atom val)
      val
    (car val)))

(defun to-keyword (symbol)
  (intern (symbol-name symbol) :keyword))
                                                                            
(defun remove-plist (plist &rest keys)
  "Remove the keys from the plist.
Useful for re-using the &REST arg after removing some options."
  (do (copy rest)
      ((null (setq rest (nth-value 2 (get-properties plist keys))))
       (nreconc copy plist))
    (do () ((eq plist rest))
      (push (pop plist) copy)
      (push (pop plist) copy))
    (setq plist (cddr plist))))

From: Sashank Varma
Subject: Re: getting commas into a macro-defining-macro expansion
Date: 
Message-ID: <none-7C3AB2.12285626052004@news.vanderbilt.edu>
In article <···············@five-percent-nation.mit.edu>,
 rif <···@mit.edu> wrote:

> I'm trying to write a macro-defining-macro in order to put a nice
> facade on top of my CL-to-R gateway.

Your project sounds interesting.  Is there a webpage
somewhere where I can read more about it?  (Google
just turned up a few of your messages on various
mailing lists.)
From: rif
Subject: Re: getting commas into a macro-defining-macro expansion
Date: 
Message-ID: <wj08yffxen7.fsf@five-percent-nation.mit.edu>
Sashank Varma <····@vanderbilt.edu> writes:

> In article <···············@five-percent-nation.mit.edu>,
>  rif <···@mit.edu> wrote:
> 
> > I'm trying to write a macro-defining-macro in order to put a nice
> > facade on top of my CL-to-R gateway.
> 
> Your project sounds interesting.  Is there a webpage
> somewhere where I can read more about it?  (Google
> just turned up a few of your messages on various
> mailing lists.)

There is no website, the project is not currently released.

Briefly, it's a way to call R functions from CL.  I built it primarily
so I could make high-quality plots directly from CL, although it will
probably eventually have other uses as well.  I used Rpy as a
template, although they get many things for free because Python
extensions are written in C, although right now my interface is still
much smaller, because Lisp is much higher-level than C and I don't do
as many different kinds of conversion (my base interface is about 650
LOC, with the majority of that being direct UFFI conversions of C
declarations).

It's mostly working right now, but I want to play with it a while
longer and see how it works before I think about releasing it.  It's
written using UFFI, but may require a bit of implementation specific
tweaking (for instance, for CMUCL, which I use, it won't work unless
you first adjust the :floating-point-traps).  Also, if you try to pass
the R interpreter an ill-formed expression, this can easily cause your
CL to segfault and become wedged.  Anyways, I hope to release this
eventually, but I'll have to see if it's actually useful.

Cheers,

rif
From: Kaz Kylheku
Subject: Re: getting commas into a macro-defining-macro expansion
Date: 
Message-ID: <cf333042.0405261506.210508af@posting.google.com>
rif <···@mit.edu> wrote in message news:<···············@five-percent-nation.mit.edu>...

> So I don't know how to get ,sequence instead of sequence in the expansion,
> and similarly I need
>  
> :xlab ,xlab :ylab ,ylab
>  
> instead of
>  
> :xlab xlab :ylab ylab

The problem is that this list is variable, generated by a mapcar. The
backquote syntax lets you specify only a static configuration of
unquotes, not a dynamically generated one. To make dynamically
generated unquotes, you need backquote to have a target syntax, so you
can generate that syntax. Backquote doesn't have such a portable
target syntax at all; the only standard interface is the backquote
reader.

There is a special case exception, in that in the nested backquote
syntax:

  ``(,,@EXPR)

the effect of the ,,@ is as if the unquote is distributed into the
results of evaluating the EXPR. For instance if EXPR is

   (LIST '(* 2 2) '(* 3 3))

then through two rounds of the backquote evaluation, the processing is
as if this was being done:

   ;; first round, EXPR evaluated, spliced and unquote distributed
over it:
   -->  `(,(* 2 2) ,(* 3 3))

   ;; second round, elements evaluated individually:
   -->  (4 9)

This unquote distribution is rather limited. But perhaps it can be
leveraged somehow in your problem.

First, the unquote is distributed over each element. If you want to
distribute it only into every second element, you can perhaps add a
quote to cancel the unquote.

Let's try this:

 ``(,,@(mapcan (lambda (indicator value)
                  `((quote ,indicator) ,value))
               '(:xlab :ylab :zlab)
               '(xlab ylab zlab)))


Let's see, the MAPCAN gives us (':xlab xlab ':ylab ylab ':zlab zlab).
Unquotes are distributed and it is spliced so we get, effectively:

  `(,':xlab ,xlab ,':ylab ,ylab ,':zlab ,zlab)

Now the commas effectively cancel the unquotes, so this is effectively
the same as:

  `(:xlab ,xlab :ylab ,ylab :zlab ,zlab)

Does this possibly help? What do you think? There is a slight evil in
using MAPCAN to stitch together lists generated by backquote. Ah well.
:)

I doubt this will work right on older versions of CLISP that don't
have my new backquote implementation, or on anything which doesn't
handle the ,,@ combination properly.
From: Lars Brinkhoff
Subject: Re: getting commas into a macro-defining-macro expansion
Date: 
Message-ID: <85oeobozst.fsf@junk.nocrew.org>
rif <···@mit.edu> writes:
> (defmacro def-r-call ((macro-name r-name &rest required-args)
>                       &rest keyword-args)
>   (let* ((rest-sym (gensym "rest"))
>          (keyword-names (mapcar #'atom-or-first keyword-args))
>          (keywords (mapcar #'to-keyword keyword-names)))
>     `(defmacro ,macro-name (,@required-args
>                             &rest ,rest-sym
>                             &key ,@keyword-args)
>        `(r ,',r-name
>            ,@',required-args
>            ,@',(mapcan #'(lambda (k n) (list k n))
>                        keywords
>                        keyword-names)
>            (remove-plist ,,rest-sym ,@',keywords)))))

I'm not sure, but I think you should use
             ,,@required-args
             ,,@(mapcan #'(lambda (k n) (list k n))
instead.

-- 
Lars Brinkhoff,         Services for Unix, Linux, GCC, HTTP
Brinkhoff Consulting    http://www.brinkhoff.se/
From: Kaz Kylheku
Subject: Re: getting commas into a macro-defining-macro expansion
Date: 
Message-ID: <cf333042.0405261518.ab5d381@posting.google.com>
Lars Brinkhoff <·········@nocrew.org> wrote in message news:<··············@junk.nocrew.org>...
> rif <···@mit.edu> writes:
> > (defmacro def-r-call ((macro-name r-name &rest required-args)
> >                       &rest keyword-args)
> >   (let* ((rest-sym (gensym "rest"))
> >          (keyword-names (mapcar #'atom-or-first keyword-args))
> >          (keywords (mapcar #'to-keyword keyword-names)))
> >     `(defmacro ,macro-name (,@required-args
> >                             &rest ,rest-sym
> >                             &key ,@keyword-args)
> >        `(r ,',r-name
> >            ,@',required-args
> >            ,@',(mapcan #'(lambda (k n) (list k n))
> >                        keywords
> >                        keyword-names)
> >            (remove-plist ,,rest-sym ,@',keywords)))))
> 
> I'm not sure, but I think you should use
>              ,,@required-args
>              ,,@(mapcan #'(lambda (k n) (list k n))
> instead.

Note that this solution works because keywords are self-evaluating.
When the unquote indiscriminately distributes into the elements of the
splice, you get:

  ,:indicator1 ,value1 ,:indicator2 ,:value2 ...

Of course ,:keyword is equivalent to :keyword, so no problem.  If you
ever need this trick in a situation involving a property list with
non-keyword indicators, you need that protective quote in there:

   (mapcan #'(lambda (k v) (list (list 'quote k) n)) ...)

or, if you aren't scared of MAPCAN-ing over backquote-produced lists:

   (mapcan #'(lambda (k v) `(',k ,v)) ...)

:)
From: John Thingstad
Subject: Re: getting commas into a macro-defining-macro expansion
Date: 
Message-ID: <opr8mbd3w3pqzri1@mjolner.upc.no>
I fail to see why def-r-call needs to be a macro wouldn't it be easier to
let a function define the macro?


On 26 May 2004 13:08:31 -0400, rif <···@mit.edu> wrote:

>
> I'm trying to write a macro-defining-macro in order to put a nice
> facade on top of my CL-to-R gateway.  In particular, I'd like to be
> able to say
> (def-r-call r-plot (plot sequence)
>   (xlab "") ylab)
> and have this expand into the following macro definition:
> (defmacro r-plot (sequence &rest rest &key (xlab "") ylab  
> &allow-other-keys)
>   `(r plot ,sequence :xlab ,xlab :ylab ,ylab
>                      ,@(remove-plist rest :xlab :ylab)))
> I'm pretty close, but I can't quite seem to get it.  Right now, I have
> (defmacro def-r-call ((macro-name r-name &rest required-args)
>                       &rest keyword-args)
>   (let* ((rest-sym (gensym "rest"))
>          (keyword-names (mapcar #'atom-or-first keyword-args))
>          (keywords (mapcar #'to-keyword keyword-names)))
>     `(defmacro ,macro-name (,@required-args
>                             &rest ,rest-sym
>                             &key ,@keyword-args)
>        `(r ,',r-name
>            ,@',required-args
>            ,@',(mapcan #'(lambda (k n) (list k n))
>                        keywords
>                        keyword-names)
>            (remove-plist ,,rest-sym ,@',keywords)))))
>
>
> the auxiliary functions are all given at the end of this post
> (remove-plist was cribbed from clocc, props to Sam Steingold).
> The expansion is close to what I want, but clearly not right:
> CL-USER> (macroexpand-1 '(def-r-call (r-plot plot sequence) (xlab "")  
> ylab))
> (DEFMACRO R-PLOT (SEQUENCE &REST #:|rest3667| &KEY (XLAB "") YLAB)
>   `(R PLOT
>       SEQUENCE
>       :XLAB
>       XLAB
>       :YLAB
>       YLAB
>       (REMOVE-PLIST ,#:|rest3667| :XLAB :YLAB)))
> T
> The problem I have is getting a list (where the list is bound at
> compile-time) to expand into a list of arguments preceded by a comma.
> So I don't know how to get ,sequence instead of sequence in the  
> expansion,
> and similarly I need
> :xlab ,xlab :ylab ,ylab
> instead of
> :xlab xlab :ylab ylab
> Any ideas on how to achieve this are appreciated.  My knowledge of
> nested backquote is sketchy at best --- most of what works above is
> from experimentation rather than deep understanding.
> Cheers,
> rif
>
> Auxiliary functions used by the def-r-call macro:
>
> (defun atom-or-first (val)
>   (if (atom val)
>       val
>     (car val)))
>
> (defun to-keyword (symbol)
>   (intern (symbol-name symbol) :keyword))
> (defun remove-plist (plist &rest keys)
>   "Remove the keys from the plist.
> Useful for re-using the &REST arg after removing some options."
>   (do (copy rest)
>       ((null (setq rest (nth-value 2 (get-properties plist keys))))
>        (nreconc copy plist))
>     (do () ((eq plist rest))
>       (push (pop plist) copy)
>       (push (pop plist) copy))
>     (setq plist (cddr plist))))



-- 
Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
From: Barry Margolin
Subject: Re: getting commas into a macro-defining-macro expansion
Date: 
Message-ID: <barmar-85F0DB.14341926052004@comcast.dca.giganews.com>
In article <················@mjolner.upc.no>,
 "John Thingstad" <··············@chello.no> wrote:

> I fail to see why def-r-call needs to be a macro wouldn't it be easier to
> let a function define the macro?

If you want to use the macro that's being defined in the same source 
file as the def-r-call invocation, it has to be a macro so that it 
expands into a top-level defmacro.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Pascal Costanza
Subject: Re: getting commas into a macro-defining-macro expansion
Date: 
Message-ID: <c92l0k$c6v$1@newsreader2.netcologne.de>
rif wrote:

> I'm trying to write a macro-defining-macro in order to put a nice
> facade on top of my CL-to-R gateway.  In particular, I'd like to be
> able to say
>  
> (def-r-call r-plot (plot sequence)
>   (xlab "") ylab)
>  
> and have this expand into the following macro definition:
>  
> (defmacro r-plot (sequence &rest rest &key (xlab "") ylab &allow-other-keys)
>   `(r plot ,sequence :xlab ,xlab :ylab ,ylab
>                      ,@(remove-plist rest :xlab :ylab)))
>  
> I'm pretty close, but I can't quite seem to get it.

I think the solution below is right. Here is how I got there: First, I 
didn't try to use nested backquotes, but instead reverted to a list 
expression, like this:

(defmacro def-r-call ((macro-name r-name &rest required-args)
                       &rest keyword-args)
   (let* ((rest-sym (gensym "rest"))
          (keyword-names (mapcar #'atom-or-first keyword-args))
          (keywords (mapcar #'to-keyword keyword-names)))
     `(defmacro ,macro-name (,@required-args
                             &rest ,rest-sym
                             &key ,@keyword-args)
        (list* ',r-name
               ,@required-args
               ,@(mapcan #'(lambda (k n) (list k n))
                         keywords
                         keyword-names)
               (remove-plist ,rest-sym ,@keywords)))))

Then, I replaced it with a backquote expression again, and prepended all 
unquoted forms with another comma, like this:

(defmacro def-r-call ((macro-name r-name &rest required-args)
                       &rest keyword-args)
   (let* ((rest-sym (gensym "rest"))
          (keyword-names (mapcar #'atom-or-first keyword-args))
          (keywords (mapcar #'to-keyword keyword-names)))
     `(defmacro ,macro-name (,@required-args
                             &rest ,rest-sym
                             &key ,@keyword-args)
        `(r ,',r-name
               ,,@required-args
               ,,@(mapcan #'(lambda (k n) (list k n))
                         keywords
                         keyword-names)
               (remove-plist ,,rest-sym ,,@keywords)))))

I hope this helps.


Pascal

-- 
1st European Lisp and Scheme Workshop
June 13 - Oslo, Norway - co-located with ECOOP 2004
http://www.cs.uni-bonn.de/~costanza/lisp-ecoop/
From: Sergey Kataev
Subject: Re: getting commas into a macro-defining-macro expansion
Date: 
Message-ID: <40B7A928.9010600@sorry.no.spam>
Hi all,

it may be loosely related, but I have encountered a similar construct in 
CLISP's mit-clx library, file input.lisp. It didn't work as intended, so 
here is what was there buried under two backquotes:

   ,@',(mapcar #'macroexpand get-code)

Reader-macro ' made:
   ,@(quote ,(mapcar #'macroexpand get-code))

So, it first expanded to
   ,(QUOTE) <perhaps result of (MAPCAR #'macroexpand get-code)>

and second expansion failed due to lack of args to (QUOTE)

What was meant is actually:
   ,@(mapcar #'macroexpand ',get-code)

producing
   ,@(mapcar #'macroexpand <whatever was in get-code>)


Regards,
   Sergey