Example:
(defgeneric foo (instance &rest initargs &key &allow-other-keys))
(defmethod foo ((cons cons) &key (car (car cons)) (cdr (cdr cons))) (setf (car cons) car (cdr cons) cdr) cons)
(defmethod foo [])
where [] is the point shows
(defmethod foo (instance &rest initargs &key (cdr (cdr cons)) (car (car cons)) (imagpart (imagpart complex)) (realpart (realpart complex)) &allow-other-keys) &body body)
in the minibuffer. Showing the CAR, CDR, IMAGPART and REALPART keys seems pretty pointless, and showing their initforms doubly so. Could we stick to the DEFGENERIC's lambda-list there, please? It's the one the method has to be congruent with.
A related, but more blue-sky issue: In case of
(foo [])
the situation is better, but still having keywords from both lists is confusing. What could be really neat is that if after
(foo x :car [])
the minibuffer would display only the lambda lists for the methods for which :CAR is a legal keyword argument.
Cheers,
-- Nikodemus
On Sat, 9 May 2009 13:48:41 +0300, Nikodemus Siivola said:
Example:
(defgeneric foo (instance &rest initargs &key &allow-other-keys))
(defmethod foo ((cons cons) &key (car (car cons)) (cdr (cdr cons))) (setf (car cons) car (cdr cons) cdr) cons)
<snip> A related, but more blue-sky issue: In case of
(foo [])
the situation is better, but still having keywords from both lists is confusing. What could be really neat is that if after
(foo x :car [])
the minibuffer would display only the lambda lists for the methods for which :CAR is a legal keyword argument.
It's a minefield, because the &allow-other-keys makes all keywords legal for all methods...
__Martin
2009/5/11 Martin Simmons martin@lispworks.com:
It's a minefield, because the &allow-other-keys makes all keywords legal for all methods...
Right. My example was bad due to the &allow-other-keys. Here's a bit of code that does what I want in SBCL -- portable version needs %SPLIT-ARGLIST and FUNCTION-KEYWORD-PARAMETERS (which can be replaced with FUNCTION-KEYWORDS.)
No comments on how tricky it would be too hook up to slime:
Examples:
(defgeneric foo (object &rest initargs &key))
(generic-function-lambda-list-using-keys #'foo nil) ; => (OBJECT &REST INITARGS &KEY)
(defmethod foo ((cons cons) &key car cdr) (cons car cdr))
(defmethod foo ((complex complex) &key realpart imagpart) (complex realpart imagpart))
(generic-function-lambda-list-using-keys #'foo nil) ; => (OBJECT &REST INITARGS &KEY CDR CAR IMAGPART REALPART) (generic-function-lambda-list-using-keys #'foo '(:realpart)) ; => (OBJECT &REST INITARGS &KEY REALPART IMAGPART) (generic-function-lambda-list-using-keys #'foo '(:cdr)) ; => (OBJECT &REST INITARGS &KEY CAR CDR) (generic-function-lambda-list-using-keys #'foo '(:zot)) ; => (OBJECT &REST INITARGS)
Code:
(in-package :sb-pcl)
;; Adapted from SB-PCL::GENERIC-FUNCTION-PRETTY-ARGLIST (defmethod generic-function-lambda-list-using-keys (generic-function req-keys) (let ((gf-lambda-list (generic-function-lambda-list generic-function)) (methods (generic-function-methods generic-function))) (if (null methods) gf-lambda-list (multiple-value-bind (gf.required gf.optional gf.rest gf.keys gf.allowp) (%split-arglist gf-lambda-list) ;; Possibly extend the keyword parameters of the gf by additional ;; key parameters of those methods that include requested keys not ;; in the generic function lambda-list. (let ((methods.keys nil) (methods.allowp nil)) (dolist (m methods) (multiple-value-bind (m.keywords m.allow-other-keys) (function-keywords m) (when (or gf.allowp m.allow-other-keys (every (lambda (key) (member key m.keywords :key #'maybe-car)) req-keys)) (multiple-value-bind (keyparams allowp) (function-keyword-parameters m) (setq methods.keys (union methods.keys keyparams :key #'maybe-car)) (setq methods.allowp (or methods.allowp allowp)))))) (let ((arglist '())) (when (or gf.allowp methods.allowp) (push '&allow-other-keys arglist)) (when (or gf.keys methods.keys) ;; We make sure that the keys of the gf appear before ;; those of its methods, since they're probably more ;; generally appliable. (setq arglist (nconc (list '&key) gf.keys (nset-difference methods.keys gf.keys) arglist))) (when gf.rest (setq arglist (nconc (list '&rest gf.rest) arglist))) (when gf.optional (setq arglist (nconc (list '&optional) gf.optional arglist))) (nconc gf.required arglist)))))))
Perhaps Slime could:
1. Use GENERIC-FUNCTION-LAMBDA-LIST for DEFMETHOD lambda-list hinting. Collecting keywords from all methods is not helpful there as far as I can see.
2. Use something akin to above for GF call lambda-list hinting -- passing in the list of keys already typed as the second argument.
Cheers,
-- Nikodemus