Thanks for all your replies.
> Taking a step back and looking for a pattern in the methods of your
> COLLECT (which I have not quoted), the key abstraction appears to be
> based on MAP.
>
> So (MAP* INTO WITH COLLECTION) would likely be the GF you should be
> using, and dispatch on its arguments.
COLLECT was just a trivial example I used to illustrate what I was after.
> I have an idea how to do this as an extension based on the CLOS MOP, but
> it's not trivial.
Indeed, my knowledge of the MOP is not nearly sophisticated enough to try my hand at that.
> A macro could do the job, although I think it's
> better to have a delegation function instead (no generic function needed
> as the main entry point), and then define a compiler macro on top for
> the common cases...
I just ended up rolling my own macro to handle wrapping a generic function in a delegating function.
(defmacro defdelegate (name lambda-list (&key generic-function) &body body)
(let ((has-rest-p (cdr (member '&rest lambda-list)))
(args (remove-if (lambda (arg)
(or (eql arg '&key)
(eql arg '&optional)
(eql arg '&rest)))
lambda-list)))
`(progn
(defun ,name ,lambda-list
,(if has-rest-p
`(apply ,generic-function ,@args)
`(,generic-function ,@args)))
(defgeneric ,generic-function ,args ,@body))))
Example of it at work:
(defdelegate collect (collect &key with into)
(:generic-function collect*)
(:method ((collection sequence) (with function) into)
(map into with collection))
(:method ((collection sequence) (with function) (into (eql 'hash-table)))
(let ((hash (make-hash-table :test #'equal)))
(mapc (lambda (assoc-pair)
(setf (gethash (car assoc-pair) hash)
(funcall with (cdr assoc-pair))))
collection)
hash)))