From: proton
Subject: CLOS defining method based on slot value
Date: 
Message-ID: <368f49bf-2429-4462-8eed-09bab74987df@a35g2000prf.googlegroups.com>
I want to define a method whose arguments are not only the class of
the object, but also some values of its slots. Is there any better way
to do this than the following?:

(defmethod ((instance my-class))
  (case (slot-name instance)
     (value1 ...
     (value2 ... )))


Since each of the cases is quite lengthy, I would like to break it up
into several methods and have something like the following:

(defmethod ((instance my-class) (eql (slot-name instance) value1))
 ...

(defmethod ((instance my-class) (eql (slot-name instance) value2))
 ...

Is this possible?
TIA

From: Ken Tilton
Subject: Re: CLOS defining method based on slot value
Date: 
Message-ID: <4766a056$0$31129$607ed4bc@cv.net>
proton wrote:
> I want to define a method whose arguments are not only the class of
> the object, but also some values of its slots. Is there any better way
> to do this than the following?:
> 
> (defmethod <did we forget something?> ((instance my-class))

   ...or are you opening there is an anonymous defmethod?

>   (case (slot-name instance)
>      (value1 ...
>      (value2 ... )))
> 
> 
> Since each of the cases is quite lengthy, I would like to break it up
> into several methods and have something like the following:
> 
> (defmethod ((instance my-class) (eql (slot-name instance) value1))
>  ...
> 
> (defmethod ((instance my-class) (eql (slot-name instance) value2))
>  ...
> 
> Is this possible?

Kind of, and it is regrettable you chose slot-name as an example. Stuck 
with it:

(defmethod slot-name :around ((i my-class))
    (slot-name-using-value i (call-next-method)))

Might DWYM. You could also make the method primary and then use 
slot-value to get the stored slot-name.

kt

-- 
http://www.theoryyalgebra.com/

"In the morning, hear the Way;
  in the evening, die content!"
                     -- Confucius
From: Maciej Katafiasz
Subject: Re: CLOS defining method based on slot value
Date: 
Message-ID: <fk69u4$6k0$3@news.net.uni-c.dk>
Den Mon, 17 Dec 2007 04:56:12 -0800 skrev proton:

> I want to define a method whose arguments are not only the class of the
> object, but also some values of its slots. Is there any better way to do
> this than the following?:
> 
> (defmethod ((instance my-class))
>   (case (slot-name instance)
>      (value1 ...
>      (value2 ... )))
> 
> 
> Since each of the cases is quite lengthy, I would like to break it up
> into several methods and have something like the following:
> 
> (defmethod ((instance my-class) (eql (slot-name instance) value1))
>  ...
> 
> (defmethod ((instance my-class) (eql (slot-name instance) value2))
>  ...
> 
> Is this possible?

No, since EQL specialisers are defined to take their value at the time of 
definition, not the time of dispatch. Also, your syntax is a bit screwy, 
you're missing method names.

However, with a bit of macrology and trivial code walking, you could do 
something like this:

(with-slot-specialisers ((slot-name instance))
  (defmethod do-foo ((obj foo) (?eql slot-name value1))
    (foo))
  
  (defmethod do-bar ((obj foo) (?eql slot-name value2))
    (bar))
  
  (defmethod do-baz ((obj foo) (?eql slot-name value3))
    (baz)))

Depending on the amount of code you have to write, it might or might not 
be worth it. Would the above do?

Cheers,
Maciej
From: proton
Subject: Re: CLOS defining method based on slot value
Date: 
Message-ID: <a39ceadd-7b55-4de6-a9f8-1a628acda749@d4g2000prg.googlegroups.com>
On Dec 17, 5:59 pm, Maciej Katafiasz <········@gmail.com> wrote:
> Den Mon, 17 Dec 2007 04:56:12 -0800 skrev proton:
>
>
>
> > I want to define a method whose arguments are not only the class of the
> > object, but also some values of its slots. Is there any better way to do
> > this than the following?:
>
> > (defmethod ((instance my-class))
> >   (case (slot-name instance)
> >      (value1 ...
> >      (value2 ... )))
>
> > Since each of the cases is quite lengthy, I would like to break it up
> > into several methods and have something like the following:
>
> > (defmethod ((instance my-class) (eql (slot-name instance) value1))
> >  ...
>
> > (defmethod ((instance my-class) (eql (slot-name instance) value2))
> >  ...
>
> > Is this possible?
>
> No, since EQL specialisers are defined to take their value at the time of
> definition, not the time of dispatch. Also, your syntax is a bit screwy,
> you're missing method names.
>
> However, with a bit of macrology and trivial code walking, you could do
> something like this:
>
> (with-slot-specialisers ((slot-name instance))
>   (defmethod do-foo ((obj foo) (?eql slot-name value1))
>     (foo))
>
>   (defmethod do-bar ((obj foo) (?eql slot-name value2))
>     (bar))
>
>   (defmethod do-baz ((obj foo) (?eql slot-name value3))
>     (baz)))
>
> Depending on the amount of code you have to write, it might or might not
> be worth it. Would the above do?
>
> Cheers,
> Maciej

Thank you both (Ken and Maciej) for your replies. Sorry about my
forgetting to write the method's name. I meant : (defmethod my-method
((instance my-class)) ...)

I am still pondering what to do, because I'm quite novice and the
solutions you propose seem rather daunting, especially Maciej's.
Further, would this solution apply to (defmethod (setf my-slot) ...)?
My main problem is in fact that I can't find a comprehensive tutorial
on CLOS, or at least, one that I can understand. So, sorry to keep
bothering you with questions, and thanks a lot for your help and your
patience.
From: Ken Tilton
Subject: Re: CLOS defining method based on slot value
Date: 
Message-ID: <47670561$0$31165$607ed4bc@cv.net>
proton wrote:
> On Dec 17, 5:59 pm, Maciej Katafiasz <········@gmail.com> wrote:
> 
>>Den Mon, 17 Dec 2007 04:56:12 -0800 skrev proton:
>>
>>
>>
>>
>>>I want to define a method whose arguments are not only the class of the
>>>object, but also some values of its slots. Is there any better way to do
>>>this than the following?:
>>
>>>(defmethod ((instance my-class))
>>>  (case (slot-name instance)
>>>     (value1 ...
>>>     (value2 ... )))
>>
>>>Since each of the cases is quite lengthy, I would like to break it up
>>>into several methods and have something like the following:
>>
>>>(defmethod ((instance my-class) (eql (slot-name instance) value1))
>>> ...
>>
>>>(defmethod ((instance my-class) (eql (slot-name instance) value2))
>>> ...
>>
>>>Is this possible?
>>
>>No, since EQL specialisers are defined to take their value at the time of
>>definition, not the time of dispatch. Also, your syntax is a bit screwy,
>>you're missing method names.
>>
>>However, with a bit of macrology and trivial code walking, you could do
>>something like this:
>>
>>(with-slot-specialisers ((slot-name instance))
>>  (defmethod do-foo ((obj foo) (?eql slot-name value1))
>>    (foo))
>>
>>  (defmethod do-bar ((obj foo) (?eql slot-name value2))
>>    (bar))
>>
>>  (defmethod do-baz ((obj foo) (?eql slot-name value3))
>>    (baz)))
>>
>>Depending on the amount of code you have to write, it might or might not
>>be worth it. Would the above do?
>>
>>Cheers,
>>Maciej
> 
> 
> Thank you both (Ken and Maciej) for your replies. Sorry about my
> forgetting to write the method's name. I meant : (defmethod my-method
> ((instance my-class)) ...)
> 
> I am still pondering what to do, because I'm quite novice and the
> solutions you propose seem rather daunting, especially Maciej's.

I suggested:

(defmethod slot-name :around ((i my-class))
    (slot-name-using-value i (call-next-method)))

...omitting the fact that you would then:

(defmethod slot-name-using-value ((i my-class)
                            (value (eql <first-value>)))

     <body for first-value>)

Now we learn you want it to be my-method. OK:

(defmethod my-name ((i my-class))
    (any-gf-name i (slot-name i)))

(defmethod any-gf-name ((i my-class)
                            (value (eql <first-value>)))
     <body for first-value>)

> Further, would this solution apply to (defmethod (setf my-slot) ...)?

Yes, though:

(defmethod (setf my-method) (new-value (i my-class)
                            (value (eql <first-value>)))
     <body for first-value>)

You mentioned elsewhere this is "daunting". Your biggest problem may be 
being too easily daunted. This is more sophisticated than noobs commonly 
mess with, but it seems to be an important problem to you, so Just 
Figure It Out. All learning roads lead to mastery, and it is better to 
learn in the context of problems you care about then just read chapters 
in order. So this is where you are. I imagine PCL covers CLOS. Ch16, it 
seems.

kt

-- 
http://www.theoryyalgebra.com/

"In the morning, hear the Way;
  in the evening, die content!"
                     -- Confucius
From: Maciej Katafiasz
Subject: Re: CLOS defining method based on slot value
Date: 
Message-ID: <fk6k9r$7d6$1@news.net.uni-c.dk>
Den Mon, 17 Dec 2007 11:32:21 -0800 skrev proton:

> Thank you both (Ken and Maciej) for your replies. Sorry about my
> forgetting to write the method's name. I meant : (defmethod my-method
> ((instance my-class)) ...)
> 
> I am still pondering what to do, because I'm quite novice and the
> solutions you propose seem rather daunting, especially Maciej's.
> Further, would this solution apply to (defmethod (setf my-slot) ...)? My
> main problem is in fact that I can't find a comprehensive tutorial on
> CLOS, or at least, one that I can understand. So, sorry to keep
> bothering you with questions, and thanks a lot for your help and your
> patience.

Well, my solution is a simple code-rewriting macro, so it would apply to 
pretty much anything. The idea is that you write your defmethod bodies 
inside a WITH-SLOT-SPECIALISERS form, pretending that what you want to do 
is possible, and then the macro takes your code, gathers all the clauses 
and rewrites them into one method beginning with CASE, like the one you 
wanted to avoid writing (by hand). So it'd work with (defmethod (setf 
foo)) as well, as it doesn't need to understand the code very much, just 
enough to do the rewriting, which is only a little bit.

As to Ken's solution, I can't comment, as honestly, I didn't understand 
it, and I don't have the time to RTFM now :).

If you want CLOS tutorials, there've been some recommended just a few 
threads back, some googling is sure to unearth them. Though you should 
understand the nature of my solution, it has nothing to do with CLOS as 
such, and everything to do with massaging the code with macros until it 
fits the compiler's tastes.

Cheers,
Maciej
From: Pascal Costanza
Subject: Re: CLOS defining method based on slot value
Date: 
Message-ID: <5so591F1a3agoU1@mid.individual.net>
proton wrote:
> I want to define a method whose arguments are not only the class of
> the object, but also some values of its slots. Is there any better way
> to do this than the following?:
> 
> (defmethod ((instance my-class))
>   (case (slot-name instance)
>      (value1 ...
>      (value2 ... )))
> 
> 
> Since each of the cases is quite lengthy, I would like to break it up
> into several methods and have something like the following:
> 
> (defmethod ((instance my-class) (eql (slot-name instance) value1))
>  ...
> 
> (defmethod ((instance my-class) (eql (slot-name instance) value2))
>  ...
> 
> Is this possible?

You're looking for some variation of predicate dispatch, which is not 
supported in CLOS. [You could probably extend CLOS with some subset of 
predicate dispatch to make this work, via the CLOS MOP, but it looks 
like far too much hassle.]

A much simpler solution is to split up the function into two distinct 
functions, one plain function to be called and one generic function to 
contain the method definitions.

Here is the generic function:

(defgeneric foo-definer (instance slot-value))

(defmethod foo-definer ((instance my-class) (slot-value (eql value1)))
   ...)

(defmethod foo-definer ((instance my-class) (slot-value (eql value2)))
   ...)

And here is the plain function:

(defun foo-caller (instance)
   (foo-definer instance (slot-name instance)))


You could then just call foo-caller, like (foo-caller some-instance), in 
your code, and automagically the right thing happens.

(This actually seems to be a pretty common idiom in Common Lisp, to 
define one function that assembles the objects to dispatch on, and then 
call a defining generic function with those objects that dispatches to 
the properly specialized methods.)


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/
From: Paul Donnelly
Subject: Re: CLOS defining method based on slot value
Date: 
Message-ID: <pan.2007.12.18.00.38.13.703460@sbcglobal.net>
On Mon, 17 Dec 2007 21:42:09 +0100, Pascal Costanza wrote:

> proton wrote:
>> I want to define a method whose arguments are not only the class of
>> the object, but also some values of its slots. Is there any better way
>> to do this than the following?:
>> 
>> (defmethod ((instance my-class))
>>   (case (slot-name instance)
>>      (value1 ...
>>      (value2 ... )))
>> 
> You're looking for some variation of predicate dispatch, which is not 
> supported in CLOS. [You could probably extend CLOS with some subset of 
> predicate dispatch to make this work, via the CLOS MOP, but it looks 
> like far too much hassle.]
> 
> A much simpler solution is to split up the function into two distinct 
> functions, one plain function to be called and one generic function to 
> contain the method definitions.

That's the solution I settled on not too long ago when writing a little
game (the desired action is decided earlier and deferred because all the
guys should base their decisions for that turn on the same world state).

(defgeneric act (guy)
  (:method ((guy guy))
    (with-slots (desired-action) guy
      (try guy desired-action))))

(defgeneric try (guy act)
  ...)