From: K. Ari Krupnikov
Subject: Something I don't understand about macros and special operators
Date: 
Message-ID: <86y84r9vhd.fsf@deb.lib.aero>
Newbie trying to learn about macros here.

Imagine I decided to rewrite defun:

(defmacro mydefun (funcname args &rest body)
  `(defun ,funcname ,args ,@body))

This works as expected:

CL-USER> (macroexpand-1 '(mydefun identity (arg) (arg)))
(DEFUN IDENTITY (ARG) (ARG))

Further:

CL-USER> (macroexpand-1 '(mydefun mapcar-1 (func list)
                           (mapcar func list)))
(DEFUN MAPCAR-1 (FUNC LIST) (MAPCAR FUNC LIST))

But if I change the name of a parameter form to a special operator, it
gets interpreted:

CL-USER> (macroexpand-1 '(mydefun mapcar-1 (function list)
                           (mapcar function list)))
(DEFUN MAPCAR-1 #'LIST (MAPCAR FUNCTION LIST))

This happens in CLISP 2.33.2

In SBCL, I get the expected

(DEFUN MAPCAR-1 (FUNCTION LIST) (MAPCAR FUNCTION LIST))

Interestingly, in CLISP,

CL-USER> (macroexpand-1 '(mydefun carmap (function &rest lists)
                           (apply #'mapcar function lists)))
(DEFUN CARMAP (FUNCTION &REST LISTS) (APPLY #'MAPCAR FUNCTION LISTS))

CLISP seems to be interpreting 'function' before expanding the macro,
in some cases (as if I'd written #'). Is this expected behavior? Is it
because finction is a special operator?

Ari.

-- 
Elections only count as free and trials as fair if you can lose money
betting on the outcome.

From: Mikko Heikelä
Subject: Re: Something I don't understand about macros and special operators
Date: 
Message-ID: <Pine.OSF.4.61.0510181029520.479250@kosh.hut.fi>
On Tue, 17 Oct 2005, K. Ari Krupnikov wrote:

> Newbie trying to learn about macros here.
>
> Imagine I decided to rewrite defun:
>
> (defmacro mydefun (funcname args &rest body)
>  `(defun ,funcname ,args ,@body))
>
> This works as expected:
>
> CL-USER> (macroexpand-1 '(mydefun identity (arg) (arg)))
> (DEFUN IDENTITY (ARG) (ARG))
>
> Further:
>
> CL-USER> (macroexpand-1 '(mydefun mapcar-1 (func list)
>                           (mapcar func list)))
> (DEFUN MAPCAR-1 (FUNC LIST) (MAPCAR FUNC LIST))
>
> But if I change the name of a parameter form to a special operator, it
> gets interpreted:
>
> CL-USER> (macroexpand-1 '(mydefun mapcar-1 (function list)
>                           (mapcar function list)))
> (DEFUN MAPCAR-1 #'LIST (MAPCAR FUNCTION LIST))
>
> This happens in CLISP 2.33.2
>
> In SBCL, I get the expected
>
> (DEFUN MAPCAR-1 (FUNCTION LIST) (MAPCAR FUNCTION LIST))

If I understand correctly both implementations give the right 
macroexpansion.  CLISP just prints the (FUNCTION LIST) that is part of 
the result as #'LIST.

You can check it out by trying e.g.

(defvar *expansion* (macroexpand-1 '(mydefun mapcar-1 (function list)
                                       (mapcar function list))))
(consp (third *expansion*)) => T
(first (third *expansion*)) => FUNCTION

In other words, your macro should work without modification, and the 
confusing part of your example has nothing to do with macroexpansion.

I hope this helps.

   -Mikko
From: K. Ari Krupnikov
Subject: Re: Something I don't understand about macros and special operators
Date: 
Message-ID: <861x2ihhqy.fsf@deb.lib.aero>
Mikko Heikel� <········@cc.hut.fi> writes:

> On Tue, 17 Oct 2005, K. Ari Krupnikov wrote:
> 
> > CL-USER> (macroexpand-1 '(mydefun mapcar-1 (function list)
> >                           (mapcar function list)))
> > (DEFUN MAPCAR-1 #'LIST (MAPCAR FUNCTION LIST))
> >
> > This happens in CLISP 2.33.2
> >
> > In SBCL, I get the expected
> >
> > (DEFUN MAPCAR-1 (FUNCTION LIST) (MAPCAR FUNCTION LIST))
> 
> If I understand correctly both implementations give the right
> macroexpansion.  CLISP just prints the (FUNCTION LIST) that is part of
> the result as #'LIST.
...

> In other words, your macro should work without modification, and the
> confusing part of your example has nothing to do with macroexpansion.

Thank you. It completely escaped me that the printer may munge the
results on their way out. I guess I got fixated on the supposed
difficulties of doing macros right :=)

Ari.

-- 
Elections only count as free and trials as fair if you can lose money
betting on the outcome.
From: Steven M. Haflich
Subject: Re: Something I don't understand about macros and special operators
Date: 
Message-ID: <F3j5f.5007$7h7.3687@newssvr21.news.prodigy.com>
K. Ari Krupnikov wrote:

> Thank you. It completely escaped me that the printer may munge the
> results on their way out. I guess I got fixated on the supposed
> difficulties of doing macros right :=)

One could argue that the pprint-dispatch for defun is insufficiently
smart.  Is is desirable to pprint #' notation when cl:function
appears as an operator, but in other contexts, such as in a lambda list,
it is desirable to pprint it without the reader macro.  This is what
Allegro does with defun:

  cl-user(4): (pprint '(defun foo (function arg)
                         (function function) (function arg)))
  (defun foo (function arg) #'function #'arg)

This kind of pprint customization is only a very minor implementation
elegance (except when that lack of elegance confuses a programmer).

The pprint-dispatch for a defun form looks like this:

   (format stream
	  "~:<~3I~W~^ ·@_~:/excl:pprint-fill/~^ ·····························@{ ~_~W~^~}~:>"
	  form))

Enjoy figuring out how it works...
From: K. Ari Krupnikov
Subject: Re: Something I don't understand about macros and special operators
Date: 
Message-ID: <867jca9ifo.fsf@deb.lib.aero>
"Steven M. Haflich" <···@alum.mit.edu> writes:

> One could argue that the pprint-dispatch for defun is insufficiently
> smart.  Is is desirable to pprint #' notation when cl:function
> appears as an operator, but in other contexts, such as in a lambda list,
> it is desirable to pprint it without the reader macro.  This is what
> Allegro does with defun:

[...]

> This kind of pprint customization is only a very minor implementation
> elegance (except when that lack of elegance confuses a programmer).

As I've mentioned, SBCL's printer exhibits similar finesse. What
confused me most was the inconsistency between them :=) Thank G-d for
c.l.l to clear such confusion.


[format string that looks like line noise] [*]

> Enjoy figuring out how it works...

That's just crazy, man :=)

Ari.

[*] With apologies to Peter Seibel.

-- 
Elections only count as free and trials as fair if you can lose money
betting on the outcome.
From: Christophe Rhodes
Subject: Re: Something I don't understand about macros and special operators
Date: 
Message-ID: <sqfyqyqbcd.fsf@cam.ac.uk>
"Steven M. Haflich" <···@alum.mit.edu> writes:

> "~:<~3I~W~^ ·@_~:/excl:pprint-fill/~^ ·····························@{ ~_~W~^~}~:>"

Is there any reason for accessing the pprint-fill symbol from the excl
package, rather than the cl package?  Also, you have a redundant ~^
near the end, just before the ~}.  No need to make these strings any
more complicated than they need to be.

Christophe
From: Pascal Bourguignon
Subject: Re: Something I don't understand about macros and special operators
Date: 
Message-ID: <87irvt60sf.fsf@thalassa.informatimago.com>
"Steven M. Haflich" <···@alum.mit.edu> writes:
> This is what Allegro does with defun:
>
>   cl-user(4): (pprint '(defun foo (function arg)
>                          (function function) (function arg)))
>   (defun foo (function arg) #'function #'arg)

Does it print also:
    (defun foo (function arg) '(function arg) #'function #'arg)

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
You never feed me.
Perhaps I'll sleep on your face.
That will sure show you.
From: Pascal Bourguignon
Subject: Re: Something I don't understand about macros and special operators
Date: 
Message-ID: <87ach697h7.fsf@thalassa.informatimago.com>
···@lib.aero (K. Ari Krupnikov) writes:
> CLISP seems to be interpreting 'function' before expanding the macro,
> in some cases (as if I'd written #'). Is this expected behavior? Is it
> because finction is a special operator?

Not before, after.  
It's the printer that outputs #'x instead of (function x).

Hopefully, when it's read back #' is bound to the right syntax macro
that will expand #'x to (function x).

To dump data as data you must write your own print function.

For example:

(defun find-nodes (tree table)
  (cond
   ((null tree) table)
   ((gethash tree table) (incf (gethash tree table))   table)
   ((atom tree)          (incf (gethash tree table 0)) table)
   (t (incf (gethash tree table 0))
      (find-nodes (cdr tree) (find-nodes (car tree) table)))))

(defun print-identified-conses (tree  &optional (stream *standard-output*))
  (let ((table (find-nodes tree (make-hash-table :test (function eq))))
        (index 0))
    (maphash (lambda (k v)
               (if (= 1 v)
                 (remhash k table)
                 (setf (gethash k table) (- (incf index))))) table)
    (labels ((print-node (node)
                         (if (null node)
                           (princ "()")
                           (let ((index (gethash node table)))
                             (if (and index (plusp index))
                               (format stream "#~A# " index)
                               (progn
                                 (when index
                                   (setf (gethash node table) (- index))
                                   (format stream "#~A=" (- index)))
                                 (if (atom node)
                                   (princ node stream)
                                   (progn
                                     (princ "(" stream) 
                                     (print-node (car node))
                                     (princ " . " stream)
                                     (print-node (cdr node))
                                     (princ ")" stream)))))))))
      (print-node tree)
      tree)))

[14]> (print-identified-conses '(function x))
(FUNCTION . (X . ()))  ; this is printed by print-identified-conses 
#'X                    ; the result here is printed by CL:PRINT from the REPL

You can also inspect the object:

[15]> (inspect '(function x))
#'COMMON-LISP-USER::X:  Cons
 a list of length 2
0:  FUNCTION
1:  COMMON-LISP-USER::X
INSPECT-- type :h for help; :q to return to the REPL ---> :q



-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

The world will now reboot.  don't bother saving your artefacts.
From: Kaz Kylheku
Subject: Re: Something I don't understand about macros and special operators
Date: 
Message-ID: <1129741392.870793.256110@g44g2000cwa.googlegroups.com>
K. Ari Krupnikov wrote:
> CL-USER> (macroexpand-1 '(mydefun carmap (function &rest lists)
>                            (apply #'mapcar function lists)))
> (DEFUN CARMAP (FUNCTION &REST LISTS) (APPLY #'MAPCAR FUNCTION LISTS))
>
> CLISP seems to be interpreting 'function' before expanding the macro,
> in some cases (as if I'd written #'). Is this expected behavior? Is it
> because finction is a special operator?

The important thing to understand is that the input and output of the
MACROEXPAND-1 function here are both /data/, not /code/. The /data
object/ (FUNCTION X), an ordinary list, is specially recognized and
rendered as the /text notation/ #'X. That of course has to do with its
role as an operator; Lisp programmers more frequently use the #'X
notation to the (FUNCTION X) one. Similarly (QUOTE X) gets rendered as
'X. But the semantics of these operators is not involved in the
rendering in any way. It's just a ``dumb filter'' in the Lisp printer
which looks for these patterns and provides an alternate text rendering
for them.

You have to understand that #'X and (FUNCTION X) are exactly the same
object: they are the same list under the EQUAL function, and in fact if
you quote them one more time, they may even be EQ!!! That is to say, if
you write '#'X in one part of the program, and '(FUNCTION X) somewhere
else, these two equivalent list literals may get merged into one object
in the compiled image.  The two notations are equivalent in the same
sense that, for instance, #xFF and 255 are equivalent. If you type #xFF
into your Lisp, it will come back as 255, unless you fiddle with the
*print-base*.

But the object (FUNCTION &REST ARGS) cannot be rendered in the #'
notation, and so it is printed plainly. The printer has to watch out
for such anomalous cases and take care that they are printed without
loss of information.

But CLISP's printer is not so kind toward ``secret'' expressions headed
by its own internal symbols in the SYSTEM package. If you generate
these with extra arguments, they may still print using the special
notation, with your extra data disappearing:

For instance try these (I'm trying them on CLISP 2.33):

   '(SYSTEM::BACKQUOTE X)  ==>  `X

   '(SYSTEM::BACKQUOTE X Y) ==> `X      # What happened to Y?

   '(SYSTEM::BACKQUOTE X Y Z) ==> `(SYSTEM::BACKQUOTE X Y Z)

Y disappeared because the extra argument of a backquote is used for
stashing extra information during the processing of backquotes which
originally did not have any additional argument. That info must not be
printed. So the Y is assumed to be that extra info added by the
backquote processor, rather than information that came from the user.

This is okay because the user should not be using symbols in the SYSTEM
package as data anyway! So there is no reason to read and write lists
headed by SYSTEM::BACKQUOTE with perfect information fidelity.

It's kind of like sticking your fork into a toaster.