Hi everybody,
Here's some behavior I see on SBCL as well as LispWorks, so I'm assuming for now they're right in doing so. My question would be why they are.
I start with the following code which I compile and load:
;;;;;;;;;;;;;;;;;;;;;;;;
(defvar *current-qualifiers* '(:a :b :c))
(define-method-combination test () ((methods *)) (let ((selected-methods (loop for method in methods when (intersection (method-qualifiers method) *current-qualifiers*) collect method))) `(call-method ,(first selected-methods) ,(rest selected-methods))))
(defgeneric foo (thing) (:method-combination test)) (defmethod foo :a (thing) '(:red)) (defmethod foo :b (thing) '(:blue))
;;;;;;;;;;;;;;;;;;;;;;;;;;
Now, in the REPL I do the following:
CL-USER> (foo 42) (:BLUE) CL-USER> (setq *current-qualifiers* '(:a :c)) (:A :C) CL-USER> (foo 42) (:BLUE)
I almost expected this. The effective method obviously isn't computed anew but was cached. But even if I now re-evaluate the DEFINE-METHOD-COMBINATION form, (FOO 42) will still return (:BLUE). Only if I re-evaluate the DEFGENERIC form will the return value change to (:RED).
My question is if the standard somewhere allows this caching to happen. That would for example mean that any kind of "dynamic method combination" (for example based on the time of the day, just for grins) is impossible.
Or am I missing something and my interpretation is wrong?
Thanks, Edi.
Edi Weitz wrote:
Hi everybody,
Here's some behavior I see on SBCL as well as LispWorks, so I'm assuming for now they're right in doing so. My question would be why they are.
Hi Edi,
you may want to read this: http://www.didierverna.net/blog/index.php?post/2013/08/16/Lisp-Corner-Cases%...
On Tue, Jul 21, 2015 at 10:30 AM, Didier Verna didier@lrde.epita.fr wrote:
http://www.didierverna.net/blog/index.php?post/2013/08/16/Lisp-Corner-Cases%...
Ouch!
On 21 Jul 2015, at 10:30, Didier Verna didier@lrde.epita.fr wrote:
Edi Weitz wrote:
Hi everybody,
Here's some behavior I see on SBCL as well as LispWorks, so I'm assuming for now they're right in doing so. My question would be why they are.
Hi Edi,
you may want to read this: http://www.didierverna.net/blog/index.php?post/2013/08/16/Lisp-Corner-Cases%...
find-method-combination doesn’t exist for end users to retrieve method combination objects, but it exist for ensure-generic-function to retrieve it. (Same for find-class by the way: The primary purpose for find-class is so that ensure-class can find the class object. That it needs fewer arguments than find-method-combination is just incidental.)
find-method-combination accept the generic function metaobject as a parameter because that’s just how the MOP generally works: You use class metaobjects or generic function metaobjects to hang your hat on and provide your own user-defined extensions. find-method-combination additionally accepts the method combination parameters so it can return optimized method combination objects, for example to distinguish between short form / long form, or predefined (like standard) or user-defined ones. For example, a user-defined method combination may recognize that for some parameters, it behaves the same as a standard one, etc.
Method combinations are generally neglected in AMOP and only minimally specified. My guess, after reading about the history of the CLOS MOP, is that the AMOP authors thought of method combinations to be redundant and not really needed, which is actually true once you have user-defined generic functions metaobject classes, because you can do pretty much anything with the latter that you can also do with the former. Method combinations are in Common Lisp probably because at the time they were added, the AMOP was not in a usable shape yet, and there was already experience with method combinations that made them useful especially in the absence of a MOP.
I still think there is good reason to use method combinations because they are more reliably supported across CL implementations, but if you feel like you have to push them too hard, it’s probably better to switch to user-defined generic function classes.
Pascal
-- Pascal Costanza The views expressed in this email are my own, and not those of my employer.
On 21 Jul 2015, at 09:46, Edi Weitz edi@weitz.de wrote:
Hi everybody,
Here's some behavior I see on SBCL as well as LispWorks, so I'm assuming for now they're right in doing so. My question would be why they are.
I start with the following code which I compile and load:
;;;;;;;;;;;;;;;;;;;;;;;;
(defvar *current-qualifiers* '(:a :b :c))
(define-method-combination test () ((methods *)) (let ((selected-methods (loop for method in methods when (intersection (method-qualifiers method) *current-qualifiers*) collect method))) `(call-method ,(first selected-methods) ,(rest selected-methods))))
(defgeneric foo (thing) (:method-combination test)) (defmethod foo :a (thing) '(:red)) (defmethod foo :b (thing) '(:blue))
;;;;;;;;;;;;;;;;;;;;;;;;;;
Now, in the REPL I do the following:
CL-USER> (foo 42) (:BLUE) CL-USER> (setq *current-qualifiers* '(:a :c)) (:A :C) CL-USER> (foo 42) (:BLUE)
I almost expected this. The effective method obviously isn't computed anew but was cached. But even if I now re-evaluate the DEFINE-METHOD-COMBINATION form, (FOO 42) will still return (:BLUE). Only if I re-evaluate the DEFGENERIC form will the return value change to (:RED).
There is no protocol, neither in Common Lisp, nor in AMOP, that specifies what happens when a method combination is redefined. I believe an implementation would even be right to completely ignore redefinitions. What you’re seeing when you evaluate the defgeneric form again is that this triggers a reinitialize-instance of the generic function metaobject with the new method combination object.
My question is if the standard somewhere allows this caching to happen. That would for example mean that any kind of "dynamic method combination" (for example based on the time of the day, just for grins) is impossible.
The standard doesn’t /disallow/ this form of caching to happen. You can indeed not express something like “dynamic method combinations” - you should rather define your own generic function metaobject class to do something like this.
Pascal
-- Pascal Costanza The views expressed in this email are my own, and not those of my employer.