From: Big Dave
Subject: function quoting
Date: 
Message-ID: <3B246440.1050809@home.com>
Hello,

I am reading "On Lisp" and I would like to know why the following macro 
returns a 'prog' when it seems (to me) that a 'let' would be the 
"standard" way?

(defmacro do-tuples/o (parms source &body body)
   (if parms
       (let ((src (gensym)))
         `(prog ((,src ,source))
            (mapc #'(lambda ,parms ,@body)
                  ,@(map0-n #'(lambda (n)
                                `(nthcdr ,n ,src))
                            (1- (length parms))))))))

From: romeo bernardi
Subject: Re: function quoting
Date: 
Message-ID: <M44V6.25389$Vk3.243256@news2.tin.it>
"Big Dave" <·······@home.com> ha scritto nel messaggio
·····················@home.com...
> Hello,
>
> I am reading "On Lisp" and I would like to know why the following macro
> returns a 'prog' when it seems (to me) that a 'let' would be the
> "standard" way?
>
> (defmacro do-tuples/o (parms source &body body)
>    (if parms
>        (let ((src (gensym)))
>          `(prog ((,src ,source))
>             (mapc #'(lambda ,parms ,@body)
>                   ,@(map0-n #'(lambda (n)
>                                 `(nthcdr ,n ,src))
>                             (1- (length parms))))))))

It uses PROG so that it behaves similarly to DO and DO*.
You can use tags and GOs in the body of these forms.

--
Pierpaolo
From: Pierre R. Mai
Subject: Re: function quoting
Date: 
Message-ID: <874rtm3kli.fsf@orion.bln.pmsf.de>
"romeo bernardi" <········@tin.it> writes:

> "Big Dave" <·······@home.com> ha scritto nel messaggio
> ·····················@home.com...
> > Hello,
> >
> > I am reading "On Lisp" and I would like to know why the following macro
> > returns a 'prog' when it seems (to me) that a 'let' would be the
> > "standard" way?
> >
> > (defmacro do-tuples/o (parms source &body body)
> >    (if parms
> >        (let ((src (gensym)))
> >          `(prog ((,src ,source))
> >             (mapc #'(lambda ,parms ,@body)
> >                   ,@(map0-n #'(lambda (n)
> >                                 `(nthcdr ,n ,src))
> >                             (1- (length parms))))))))
> 
> It uses PROG so that it behaves similarly to DO and DO*.
> You can use tags and GOs in the body of these forms.

And more importantly RETURN, because they all create an implicit block
named nil.

_BUT_, and this is a bug, the TAGBODY part of PROG will not work as
expected in his macro expansion, since the tags have to be at
top-level w.r.t. the PROG/TAGBODY form, for them to take effect, which
is effectively prevented by wrapping the body inside the anonymous
lambda.

The better (and in this instance the only correct) way of going about
this, is to put the LET, BLOCK and TAGBODY constructs that PROG
consists off explicitly into the macro-expansion:

(defmacro do-tuples/o (parms source &body body)
   (if parms
       (let ((src (gensym)))
         `(let ((,src ,source))
            (block nil
              (mapc #'(lambda ,parms (tagbody ,@body))
                    ,@(map0-n #'(lambda (n)
                                  `(nthcdr ,n ,src))
                              (1- (length parms)))))))))

This still isn't 100% correct, though, because the user will expect to
be able to put bound declarations on the parameter variables,
i.e. this should work:

(do-tuples/o (x y) fixnum-list
  (declare (fixnum x y))
  ...)

In the original version this worked as expected, but our interposition
of TAGBODY will prevent the recognition of the declarations, and
furthermore even if they were recognized, they would no longer be
bound but free declarations.

So we have to parse the body for declarations, and pull them out of
the TAGBODY form.  Most implementations provide utility functions to
pull apart bodies, and if your implementation doesn't, you can write
one easily yourself.  If we assume a function SYSTEM:PARSE-BODY[1]
like the one in CMU CL, we can rewrite our macro like this:

(defmacro do-tuples/o (parms source &body body &environment env)
   (if parms
       (let ((src (gensym)))
         (multiple-value-bind (real-body declarations)
             (system:parse-body body env nil)
           `(let ((,src ,source))
              (block nil
                (mapc #'(lambda ,parms ,@declarations (tagbody ,@real-body))
                      ,@(map0-n #'(lambda (n)
                                    `(nthcdr ,n ,src))
                                (1- (length parms))))))))))

Furthermore, I'd make do-tuples/o more similar to the other looping
constructs in CL, by moving the parms and source parameters into their
own sub-list, and including an optional result form.  And I wouldn't
special-case the no-parms case, so we get:

(defmacro do-tuples/o 
    ((parms source &optional result) &body body &environment env)
  (let ((src (gensym)))
    (multiple-value-bind (real-body declarations)
        (system:parse-body body env nil)
      `(let ((,src ,source))
         (block nil
           (mapc #'(lambda ,parms ,@declarations (tagbody ,@real-body))
                 ,@(map0-n #'(lambda (n) `(nthcdr ,n ,src))
                           (1- (length parms))))
           (progn ,@result))))))

Regs, Pierre.


Footnotes: 
[1]  PARSE-BODY is an external symbol in the SYSTEM package.
Function: #<Function SYSTEM:PARSE-BODY {10286DB9}>
Function arguments:
  (body environment &optional (doc-string-allowed t))
Function documentation:
  This function is to parse the declarations and doc-string out of the body of
  a defun-like form.  Body is the list of stuff which is to be parsed.
  Environment is ignored.  If Doc-String-Allowed is true, then a doc string
  will be parsed out of the body and returned.  If it is false then a string
  will terminate the search for declarations.  Three values are returned: the
  tail of Body after the declarations and doc strings, a list of declare forms,
  and the doc-string, or NIL if none.

The implementation is this:

;;; Parse-Body  --  Public
;;;
;;;    Parse out declarations and doc strings, *not* expanding macros.
;;; Eventually the environment arg should be flushed, since macros can't expand
;;; into declarations anymore.
;;;
(defun parse-body (body environment &optional (doc-string-allowed t))
  "This function is to parse the declarations and doc-string out of the body of
  a defun-like form.  Body is the list of stuff which is to be parsed.
  Environment is ignored.  If Doc-String-Allowed is true, then a doc string
  will be parsed out of the body and returned.  If it is false then a string
  will terminate the search for declarations.  Three values are returned: the
  tail of Body after the declarations and doc strings, a list of declare forms,
  and the doc-string, or NIL if none."
  (declare (ignore environment))
  (let ((decls ())
        (doc nil))
    (do ((tail body (cdr tail)))
        ((endp tail)
         (values tail (nreverse decls) doc))
      (let ((form (car tail)))
        (cond ((and (stringp form) (cdr tail))
               (if doc-string-allowed
                   (setq doc form)
                   (return (values tail (nreverse decls) doc))))
              ((not (and (consp form) (symbolp (car form))))
               (return (values tail (nreverse decls) doc)))
              ((eq (car form) 'declare)
               (push form decls))
              (t
               (return (values tail (nreverse decls) doc))))))))



-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein
From: Pekka P. Pirinen
Subject: Re: function quoting
Date: 
Message-ID: <ix7kyglbmr.fsf@globalgraphics.com>
"Pierre R. Mai" <····@acm.org> writes:
> Most implementations provide utility functions to pull apart bodies,

In LispWorks:
 DSPEC:SEPARATE-DECLARATIONS forms env
   A function returning two values: the body minus the declarations,
   and the list of declarations (without DECLARE).
 DSPEC:WITH-BODY-COMPONENTS (doc-string decls forms) (body env) &body wbc-body
  A macro to parse BODY, binding a variable to each component:
  documentation string, declarations, and body forms.

> (defmacro do-tuples/o 
>    ((parms source &optional result) &body body &environment env)
>   [...]
>           (progn ,@result))))))

YM          ,result)))))
it's not an &rest.

Regarding SYSTEM:PARSE-BODY in CMU CL:
>         (cond ((and (stringp form) (cdr tail))
>                (if doc-string-allowed
>                    (setq doc form)
>                    (return (values tail (nreverse decls) doc))))

At most one doc-string is allowed, so that should say:
                     (setq doc form doc-string-allowed nil)
-- 
Pekka P. Pirinen
Global Graphics Software (incorporating Harlequin and Jaws)
Those who do not understand Lisp are doomed to reimplement it.
  - Erik Naggum <····@naggum.no>