Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Method Specializers on Keyword Arguments

32 views
Skip to first unread message

helmut.r...@gmail.com

unread,
May 21, 2012, 12:42:48 AM5/21/12
to
Is there anyway to mark specializers on the key word arguments on specializers, so that something similar to the following code would work?

(defgeneric collect (collection &key with into)

(:method ((collection sequence) &key (with function) into)
(map into with collection))

(:method ((collection sequence) &key (with function) (into (eql 'hash-table)))
(let ((hash (make-hash-table :test #'equal))
(mapc (lambda (assoc-pair)
(setf (gethash (car assoc-pair) hash) (cdr assoc-pair)))
collection)
hash)))


I've been circumventing this limitation by using a generic function that accepts keywords as the client interface which delegates to some generic function that has specialized arguments.

(defgeneric collect (collection &key with into)
(:method (collection &key with into)
(collect* collection :with with :into into)))

(defgeneric collect* (collection with into)
(:method ((collection sequence) (with function) into)
;; ... you get the picture ...
))

Is there a standard way to do this or am I gonna have to roll my own delegation macro that encapsulates this pattern?

Madhu

unread,
May 21, 2012, 2:22:25 AM5/21/12
to

* helmut.r...@gmail.com <4d65b2ce-1b80-40a5...@googlegroups.com> :
Wrote on Sun, 20 May 2012 21:42:48 -0700 (PDT):

| I've been circumventing this limitation by using a generic function
| that accepts keywords as the client interface which delegates to some
| generic function that has specialized arguments.
|
| (defgeneric collect (collection &key with into)
| (:method (collection &key with into)
| (collect* collection :with with :into into)))

[Surely you meant (COLLECT* WITH INTO) here]

| Is there a standard way to do this or am I gonna have to roll my own
| delegation macro that encapsulates this pattern?

I do not believe this "so called pattern" can be expressed in a macro,
since the massaging of arguments has to by the programmer on a case by
case (method signature by method signature) basis. Any imagined macro
would only add useless layers, which can then be deleted without any
loss to expressibility.

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.

--- Madhu

Pascal Costanza

unread,
May 21, 2012, 2:29:28 AM5/21/12
to
On 21/05/2012 06:42, helmut.r...@gmail.com wrote:
> Is there anyway to mark specializers on the key word arguments on specializers, so that something similar to the following code would work?
>
> (defgeneric collect (collection&key with into)
>
> (:method ((collection sequence)&key (with function) into)
> (map into with collection))
>
> (:method ((collection sequence)&key (with function) (into (eql 'hash-table)))
> (let ((hash (make-hash-table :test #'equal))
> (mapc (lambda (assoc-pair)
> (setf (gethash (car assoc-pair) hash) (cdr assoc-pair)))
> collection)
> hash)))
>
>
> I've been circumventing this limitation by using a generic function that accepts keywords as the client interface which delegates to some generic function that has specialized arguments.
>
> (defgeneric collect (collection&key with into)
> (:method (collection&key with into)
> (collect* collection :with with :into into)))
>
> (defgeneric collect* (collection with into)
> (:method ((collection sequence) (with function) into)
> ;; ... you get the picture ...
> ))
>
> Is there a standard way to do this or am I gonna have to roll my own delegation macro that encapsulates this pattern?

I have an idea how to do this as an extension based on the CLOS MOP, but
it's not trivial. Apart from that, I'm not aware of any library that
provides such a feature. 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...

Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
The views expressed are my own, and not those of my employer.

Barry Margolin

unread,
May 21, 2012, 9:30:02 AM5/21/12
to
In article <4d65b2ce-1b80-40a5...@googlegroups.com>,
helmut.r...@gmail.com wrote:

> Is there anyway to mark specializers on the key word arguments on
> specializers, so that something similar to the following code would work?

Specialization is only allowed for required arguments. Keyword arguments
are optional, so you can't specialize them. Squeezing specializers into
the syntax could be difficult, since specifying the argument as a list
already has a meaning: you're giving it a default value. And within
that syntax, if you replace the variable name with a list, it's used to
specify a specific keyword rather than the default of :<variable>. I
suppose we could have allowed (<var> <default> <supplied-p>
<specializer>), but that's pretty complicated for something that it
isn't even clear should be allowed in the first place. In particular,
the default value would probably have to be specified at the generic
function level, not the method, so that the proper method can be
selected when the keyword isn't provided.

>
> (defgeneric collect (collection &key with into)
>
> (:method ((collection sequence) &key (with function) into)
> (map into with collection))
>
> (:method ((collection sequence) &key (with function) (into (eql
> 'hash-table)))
> (let ((hash (make-hash-table :test #'equal))
> (mapc (lambda (assoc-pair)
> (setf (gethash (car assoc-pair) hash) (cdr assoc-pair)))
> collection)
> hash)))
>
>
> I've been circumventing this limitation by using a generic function that
> accepts keywords as the client interface which delegates to some generic
> function that has specialized arguments.
>
> (defgeneric collect (collection &key with into)
> (:method (collection &key with into)
> (collect* collection :with with :into into)))
>
> (defgeneric collect* (collection with into)
> (:method ((collection sequence) (with function) into)
> ;; ... you get the picture ...
> ))
>
> Is there a standard way to do this or am I gonna have to roll my own
> delegation macro that encapsulates this pattern?

--
Barry Margolin, bar...@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***

helmut.r...@gmail.com

unread,
May 21, 2012, 2:02:19 PM5/21/12
to
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)))
0 new messages