I am solving a problem using a numerical method, but for a specific
case an analytical solution exists. The solution will be implemented
by a generic function. I thought it would be nice to test my method
by comparing the analytical and numerical solutions for the specific
case, but I for that I need to force the numerical solution when CLOS
would dispatch to the analytical one.
(= (addto5 0) ; want to call general, but don't know how
(addto5 0)) ; specific
I can, of course, always write the numerical and the analytical
implementations as plain vanilla functions and have the generic
function call those, but it would be so nice to have a simple code in
the main library (at the price of more complex code in the unit tests
-- I imagine that I would need to use MOP for this?)
> I am solving a problem using a numerical method, but for a specific
> case an analytical solution exists. The solution will be implemented
> by a generic function. I thought it would be nice to test my method
> by comparing the analytical and numerical solutions for the specific
> case, but I for that I need to force the numerical solution when CLOS
> would dispatch to the analytical one.
> (= (addto5 0) ; want to call general, but don't know how
> (addto5 0)) ; specific
> I can, of course, always write the numerical and the analytical
> implementations as plain vanilla functions and have the generic
> function call those, but it would be so nice to have a simple code in
> the main library (at the price of more complex code in the unit tests
> -- I imagine that I would need to use MOP for this?)
When you have the MOP, you can use generic-function-methods to obtain the methods that are associated with the generic function:
> (mapcar #'method-function *)
(#<interpreted function (METHOD ADDTO5 (T)) 21B5D0DA>
#<interpreted function (METHOD ADDTO5 ((EQL 0))) 21B6E64A>)
...and those functions can be funcalled:
> (mapcar (lambda (f) (funcall f 0)) *)
(5 5)
Note, however, that Common Lisp implementations differ in what argument lists method functions accept. The above example was in LispWorks, which does not conform to the CLOS MOP specification here. The correct invocation should be this:
> (mapcar (lambda (f) (funcall f '(0) '())) *)
I strongly believe this should work in SBCL, for example, but I haven't tested it.
[According to the CLOS MOP, a method function receives the list of arguments plus a list of "next" methods that call-next-method can invoke.]
On Saturday, April 14, 2012 8:58:23 AM UTC-4, Tamas wrote:
> Hi,
> I am solving a problem using a numerical method, but for a specific
> case an analytical solution exists. The solution will be implemented
> by a generic function. I thought it would be nice to test my method
> by comparing the analytical and numerical solutions for the specific
> case, but I for that I need to force the numerical solution when CLOS
> would dispatch to the analytical one.
> (= (addto5 0) ; want to call general, but don't know how
> (addto5 0)) ; specific
> I can, of course, always write the numerical and the analytical
> implementations as plain vanilla functions and have the generic
> function call those, but it would be so nice to have a simple code in
> the main library (at the price of more complex code in the unit tests
> -- I imagine that I would need to use MOP for this?)
> Best,
> Tamas
This is only tangentially related to your question, but it may help (Pascal's answer definitely enlightened me).
I also have multiple methods calculating the same thing, using different models or approximations.
For that I use a combination of methods, special variables, and functions. Using your example, I would have addto5% method, *addto5-default-method* special variable and addto5 function:
> On Saturday, April 14, 2012 8:58:23 AM UTC-4, Tamas wrote:
>> Hi,
>> I am solving a problem using a numerical method, but for a specific
>> case an analytical solution exists. The solution will be implemented
>> by a generic function. I thought it would be nice to test my method
>> by comparing the analytical and numerical solutions for the specific
>> case, but I for that I need to force the numerical solution when CLOS
>> would dispatch to the analytical one.
>> (= (addto5 0) ; want to call general, but don't know how
>> (addto5 0)) ; specific
>> I can, of course, always write the numerical and the analytical
>> implementations as plain vanilla functions and have the generic
>> function call those, but it would be so nice to have a simple code in
>> the main library (at the price of more complex code in the unit tests
>> -- I imagine that I would need to use MOP for this?)
>> Best,
>> Tamas
> This is only tangentially related to your question, but it may help (Pascal's answer definitely enlightened me).
> I also have multiple methods calculating the same thing, using different models or approximations.
> For that I use a combination of methods, special variables, and functions. Using your example, I would have addto5% method, *addto5-default-method* special variable and addto5 function:
> (= (addto5 0) ; want to call general, but don't know how
> (addto5 0)) ; specific
Well, if you want something general to use for the case where all of the methods up the generalization hierarchy are supposed to return the same value, you could build this out of COMPUTE-APPLICABLE-METHODS, FUNCALL and the MOP accessor METHOD-FUNCTION. This version works with SBCL. You will need to adjust the package name on METHOD-FUNCTION for other lisps.
(defun check-all-methods (function &rest args)
(setq function (coerce function 'function))
(let ((all-methods (compute-applicable-methods function args)))
(reduce #'=
(loop for (method . next-methods) on all-methods by #'cdr
collect (funcall (sb-mop:method-function method)
args
next-methods)))))
On Saturday, 14 April 2012 15:58:23 UTC+3, Tamas wrote:
> I want to test something like
> (= (addto5 0) ; want to call general, but don't know how
> (addto5 0)) ; specific
> I can, of course, always write the numerical and the analytical
> implementations as plain vanilla functions and have the generic
> function call those, but it would be so nice to have a simple code in
> the main library (at the price of more complex code in the unit tests
> -- I imagine that I would need to use MOP for this?)
The way I would probably do this is via ADD-METHOD/REMOVE-METHOD:
Completely untested. Same approach could be used to write
(without-method (spec1 spec2)
...body...)
removing the method specialized on SPEC1 and SPEC2 for BODY, etc.
This /is/ a bit heavy-handed, but you are testing the internals of your own code, so...
I also suspect that the bits of MOPery required here are extremely portable. I would expect no trouble just using CLOSER-MOP for this. (I would expect no trouble using CLOSER-MOP for most anything, really, but simple and straightforward things like this should be completely trouble-free.)