From: Peter Lewerin
Subject: map macro
Date: 
Message-ID: <b72f3640.0409171100.1f68fa4b@posting.google.com>
I find it difficult to remember which behavior goes with which map
function.  Today I wrote an explanation to myself along these lines:

    (mapc #'f '(a b c) '(1 2 3))

works like

    (loop for a in '(a b c)
          and b in '(1 2 3)
          do (f a b))

while mapl in a similar call works like

    (loop for a on '(a b c)
          and b on '(1 2 3)
          do (f a b))

and

    mapcar  -- ... in ... collect
    maplist -- ... on ... collect
    mapcan  -- ... in ... nconc
    mapcon  -- ... on ... nconc

Then I thought, why not express this as a macro with the call syntax

    (map* do      #'f in '(a b c) '(1 2 3)) ; mapc
    (map* do      #'f on '(a b c) '(1 2 3)) ; mapl
    (map* collect #'f in '(a b c) '(1 2 3)) ; mapcar
    (map* collect #'f on '(a b c) '(1 2 3)) ; maplist
    (map* nconc   #'f in '(a b c) '(1 2 3)) ; mapcan
    (map* nconc   #'f on '(a b c) '(1 2 3)) ; mapcon

So:

    (defmacro map* (a f b &rest lists)
      (let ((flavor (ecase a
                      (do (ecase b
                            (in 'mapc)
                            (on 'mapl)))
                      (collect (ecase b
                                 (in 'mapcar)
                                 (on 'maplist)))
                      (nconc (ecase b
                               (in 'mapcan)
                               (on 'mapcon))))))
        `(,flavor ,f ,@lists)))

A small step for lisp-hacker-kind, but a medium-sized leap for me in
understanding macros.

Comments?
From: Will Hartung
Subject: Re: map macro
Date: 
Message-ID: <2r12uvF14jvloU1@uni-berlin.de>
"Peter Lewerin" <·············@swipnet.se> wrote in message
·································@posting.google.com...
> I find it difficult to remember which behavior goes with which map
> function.  Today I wrote an explanation to myself along these lines:

*snip*

> Then I thought, why not express this as a macro with the call syntax
>
>     (map* do      #'f in '(a b c) '(1 2 3)) ; mapc
>     (map* do      #'f on '(a b c) '(1 2 3)) ; mapl
>     (map* collect #'f in '(a b c) '(1 2 3)) ; mapcar
>     (map* collect #'f on '(a b c) '(1 2 3)) ; maplist
>     (map* nconc   #'f in '(a b c) '(1 2 3)) ; mapcan
>     (map* nconc   #'f on '(a b c) '(1 2 3)) ; mapcon

All well and good. But some may well say that this is the curse of Lisp.

By doing this, you basically wrap a fairly common idiom into a "proprietary"
structure, and you risk confusing other, traditional Lisp readers when they
see your code.

This new idiom is obvious to you because you know and understand the LOOP
structure underneath it, but for a reader didn't necessarily make that leap,
it may become more confusing.

Simply put, to understand (maplist ...), you need only understand maplist
(and it's a keystroke away in the hyperspec).

To understand (map* collect #'f on '(a b c) '(1 2 3)), you need to
understand not only your macro, but the LOOP keywords as well.

You need understand 6 (less-mnemonic) concepts in the stock case, whereas
you need understand only 5 (do, collect, nconc, in, on) (more mnemonic)
concepts for yours. It's not clear that the ease of understand your macro
outweighs the potential confusion overall. For new users, yours is probably
easier, once explained to them.

Finally, some of the mapping functions are used far more often than the
others, so the problem you are trying to solve may well be less of a problem
than at first glance.

Other than that, seems like a fine macro, though not as concise as the stock
functions.

And as a learning exercise for macros, I'm sure it did great.

Regards,

Will Hartung
(·····@msoft.com)