Subject: Re: Looping at toplevel?
From: rpw3@rpw3.org (Rob Warnock)
Date: Wed, 12 Dec 2007 20:25:24 -0600
Newsgroups: comp.lang.lisp
Message-ID: <SJmdnUsg3OqJBf3anZ2dnUVZ_oWdnZ2d@speakeasy.net>
thorne <thorne@timbral.net> wrote:
+---------------
| Actually, as soon as i posted i thought of:
| (eval 
|  (cons 'progn 
|        (loop for x in foo collect
| 	     (list 'deftag x))))
| Which seems to work, but something tells me it's not idiomatic
| code.... or is it?
+---------------

Nope. "EVAL bad."  ;-}

I have no idea *why* you want to pollute your COMMON-LISP-USER package
namespace with a bunch of generated function names... but if you insist,
consider writing a macro which expands to the above code, e.g.:

    (defmacro deftags (&rest tags)
      `(progn
        ,(loop for tag in tags
	   collect `(deftag ,tag))))

A better, more idomatic way might be to create a *single* data
structure -- a hash table or an alist or a B-tree, whatever --
which maps your "tags" to anonymous functions which close over
the unique-per-function data they will need later. This can easily
be done without any macros at all!!

    > (defvar *tagged-functions* (make-hash-table))

    *TAGGED-FUNCTIONS*
    > (defun add-tagged-function (tag)
	(assert (symbolp tag) ()
	  "This version of the code works only with symbols, not ~s" tag)
	(setf (gethash tag *tagged-functions*)
	      (lambda ()
		(format t "Hi! My name is ~s~%" tag))))

    ADD-TAGGED-FUNCTION
    > (mapc #'add-tagged-function '(a b c d e f foo bar baz charlie mike))

    (A B C D E F FOO BAR BAZ CHARLIE MIKE)
    > (funcall (gethash 'charlie *tagged-functions*))
    Hi! My name is CHARLIE
    NIL
    > 

And you don't even need macros to make this convenient to use, e.g.:

    > (defun do-tags (&rest rest)
	;; Allow *either* a single list arg or a spread list of tags 
	(when (and (listp (first rest)) (endp (rest rest)))
	  (setf rest (first rest)))
	(dolist (tag rest)
	  (funcall (gethash tag *tagged-functions*))))

    DO-TAGS
    > (do-tags 'foo)
    Hi! My name is FOO
    NIL
    > (do-tags 'foo 'bar 'mike)
    Hi! My name is FOO
    Hi! My name is BAR
    Hi! My name is MIKE
    NIL
    > (do-tags '(baz charlie d c))
    Hi! My name is BAZ
    Hi! My name is CHARLIE
    Hi! My name is D
    Hi! My name is C
    NIL
    > (mapc #'do-tags (append '(foo mike) '(a b)))
    Hi! My name is FOO
    Hi! My name is MIKE
    Hi! My name is A
    Hi! My name is B
    (FOO MIKE A B)
    > 


-Rob

p.s. Exercise for the student: As it stands, this will fail:

    > (add-tagged-function "gorp")

    Error in function LISP::ASSERT-ERROR:
       This version of the code works only with symbols, not "gorp"
       [Condition of type SIMPLE-ERROR]

    Restarts:
      0: [CONTINUE] Retry assertion.
      1: [ABORT   ] Return to Top-Level.
    ...[etc.]...

Bonus question: Not only that, make it so that FOO, :FOO, "foo",
and "FOO" are all considered to be the "same" tag, regardless of
the current (READTABLE-CASE *READTABLE*).

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607