From: rif
Subject: oddp and apply
Date: 
Message-ID: <wj07k5eiog6.fsf@five-percent-nation.mit.edu>
I'm looking at gnuplot.lisp in CLOCC, with an eye to adding some
functionality.  I encountered the following macro:

(defmacro with-plot-stream ((str &rest options) &body body)
  "Execute body, with STR bound to the gnuplot stream.
Usage: (with-plot-stream (stream :plot PLOT &rest OPTIONS) body).
OPTIONS are gnuplot(1) options, the following are accepted:
 XLABEL YLABEL TIMEFMT XDATA DATA-STYLE TITLE XBEG XEND GRID TERM
 BORDER LEGEND (key in gnuplot)
PLOT means:
  :plot     => plot;
  :print   => print;
  :wait    => plot and wait for gnuplot to terminate;
  :file    => write `*gnuplot-file*' and print a message;
  pathname designator => write this file and print a message;
  stream   => write gnuplot commands to this stream;
  NIL      => do nothing, print nothing, return NIL."
  (with-gensyms ("WPS-" body-function)
    `(flet ((,body-function (,str) ,@body))
      ;; this cannot be replaced with a simple inline funcall since
      ;; OPTIONS are not necessarily known at compile time
      ,(if (oddp (length options))
           `(apply #'internal-with-plot-stream #',body-function ,@options)
           `(internal-with-plot-stream #',body-function ,@options)))))

I'm having trouble understanding the purpose of the last three lines,
as well as the comment before it.  It seems to me that the @ in
@options will always cause that to be evaluated at compile
(macroexpansion) time.  Also, I'm unclear why we'd want to use apply
if the length of options is odd, but call the same function directly
otherwise.  Can anyone help explain what's going on here?

Cheers,

rif
From: Barry Margolin
Subject: Re: oddp and apply
Date: 
Message-ID: <rFc%a.138$mD.50@news.level3.com>
In article <···············@five-percent-nation.mit.edu>,
rif  <···@mit.edu> wrote:
>
>I'm looking at gnuplot.lisp in CLOCC, with an eye to adding some
>functionality.  I encountered the following macro:
>
>(defmacro with-plot-stream ((str &rest options) &body body)
>  "Execute body, with STR bound to the gnuplot stream.
>Usage: (with-plot-stream (stream :plot PLOT &rest OPTIONS) body).
>OPTIONS are gnuplot(1) options, the following are accepted:
> XLABEL YLABEL TIMEFMT XDATA DATA-STYLE TITLE XBEG XEND GRID TERM
> BORDER LEGEND (key in gnuplot)
>PLOT means:
>  :plot     => plot;
>  :print   => print;
>  :wait    => plot and wait for gnuplot to terminate;
>  :file    => write `*gnuplot-file*' and print a message;
>  pathname designator => write this file and print a message;
>  stream   => write gnuplot commands to this stream;
>  NIL      => do nothing, print nothing, return NIL."
>  (with-gensyms ("WPS-" body-function)
>    `(flet ((,body-function (,str) ,@body))
>      ;; this cannot be replaced with a simple inline funcall since
>      ;; OPTIONS are not necessarily known at compile time
>      ,(if (oddp (length options))
>           `(apply #'internal-with-plot-stream #',body-function ,@options)
>           `(internal-with-plot-stream #',body-function ,@options)))))
>
>I'm having trouble understanding the purpose of the last three lines,
>as well as the comment before it.  It seems to me that the @ in
>@options will always cause that to be evaluated at compile
>(macroexpansion) time.  Also, I'm unclear why we'd want to use apply
>if the length of options is odd, but call the same function directly
>otherwise.  Can anyone help explain what's going on here?

It looks to me like this is intended to handle the case where the user
provides an expression for the options rather than the options themselves:

(defvar *plot-options* '(:wait t))

(with-plot-stream (stream *plot-options*)
  ...)

This expands into:

(apply #'internal-with-plot-stream #'(lambda ...) *plot-options*)

By using ODDP, it allows for a combination, e.g.

(with-plot-stream (stream :xlabel "X" *plot-options*)
  ...)

This usage doesn't seem to be documented -- maybe it's a backward
compatibility feature.

-- 
Barry Margolin, ··············@level3.com
Level(3), Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.