From: Tamas K Papp
Subject: chaining accessors in generic function
Date: 
Message-ID: <6ql6cjFcv4qmU1@mid.individual.net>
Hi,

Suppose I have an object and an associated method.  Then another
object which contains exactly one instance of the previous object (+
some extra stuff, not presented in the example below), so I can call
the same method on it, and so on.  Simplified example:

(defclass a ()
  ((text :initform "foo" :accessor text)))

(defclass b ()
  ((a :initarg :a :accessor a-of)))

(defclass c ()
  ((b :initarg :b :accessor b-of)))

(defgeneric say (object)
  (:method ((a a))
    (format t "text is ~a" (text a)))
  (:method ((b b))
    (say (a-of b)))
  (:method ((c c))
    (say (b-of c))))

(defparameter *a* (make-instance 'a))
(say *a*) ;; prints "text is foo"
(defparameter *b* (make-instance 'b :a *a*))
(say *b*) ;; prints "text is foo"
(defparameter *c* (make-instance 'c :b *b*))
(say *c*) ;; prints "text is foo"

But this is a bit inelegant, because I have to use "say" explicitly in
the defgeneric form above.  Is there a way around this?  I read about
call-next-method, but that is for something else.

Thank you,

Tamas

From: Rainer Joswig
Subject: Re: chaining accessors in generic function
Date: 
Message-ID: <joswig-0CFBB5.21342514122008@news-europe.giganews.com>
In article <··············@mid.individual.net>,
 Tamas K Papp <······@gmail.com> wrote:

> Hi,
> 
> Suppose I have an object and an associated method.  Then another
> object which contains exactly one instance of the previous object (+
> some extra stuff, not presented in the example below), so I can call
> the same method on it, and so on.  Simplified example:
> 
> (defclass a ()
>   ((text :initform "foo" :accessor text)))
> 
> (defclass b ()
>   ((a :initarg :a :accessor a-of)))
> 
> (defclass c ()
>   ((b :initarg :b :accessor b-of)))
> 
> (defgeneric say (object)
>   (:method ((a a))
>     (format t "text is ~a" (text a)))
>   (:method ((b b))
>     (say (a-of b)))
>   (:method ((c c))
>     (say (b-of c))))
> 
> (defparameter *a* (make-instance 'a))
> (say *a*) ;; prints "text is foo"
> (defparameter *b* (make-instance 'b :a *a*))
> (say *b*) ;; prints "text is foo"
> (defparameter *c* (make-instance 'c :b *b*))
> (say *c*) ;; prints "text is foo"
> 
> But this is a bit inelegant, because I have to use "say" explicitly in
> the defgeneric form above.  Is there a way around this?  I read about
> call-next-method, but that is for something else.
> 
> Thank you,
> 
> Tamas

something like:

(defclass b ()
  ((a :initarg :a :accessor a-of :reader next)))

(defclass c ()
  ((b :initarg :b :accessor b-of :reader next)))

(defmethod say (object)
   (say (next object)))

or

(defclass say-mixin () ())

(defmethod say ((object say-mixin))
  (say (next object)))

(defclass b (say-mixin)
  ((a :initarg :a :accessor a-of :reader next)))

-- 
http://lispm.dyndns.org/
From: Thomas A. Russ
Subject: Re: chaining accessors in generic function
Date: 
Message-ID: <ymifxkpco64.fsf@blackcat.isi.edu>
Tamas K Papp <······@gmail.com> writes:

> Hi,
> 
> Suppose I have an object and an associated method.  Then another
> object which contains exactly one instance of the previous object (+
> some extra stuff, not presented in the example below), so I can call
> the same method on it, and so on.  Simplified example:
> 
> (defclass a ()
>   ((text :initform "foo" :accessor text)))
> 
> (defclass b ()
>   ((a :initarg :a :accessor a-of)))
> 
> (defclass c ()
>   ((b :initarg :b :accessor b-of)))
> 
> (defgeneric say (object)
>   (:method ((a a))
>     (format t "text is ~a" (text a)))
>   (:method ((b b))
>     (say (a-of b)))
>   (:method ((c c))
>     (say (b-of c))))
> 
> (defparameter *a* (make-instance 'a))
> (say *a*) ;; prints "text is foo"
> (defparameter *b* (make-instance 'b :a *a*))
> (say *b*) ;; prints "text is foo"
> (defparameter *c* (make-instance 'c :b *b*))
> (say *c*) ;; prints "text is foo"
> 
> But this is a bit inelegant, because I have to use "say" explicitly in
> the defgeneric form above.  Is there a way around this?  I read about
> call-next-method, but that is for something else.

Well, I don't think it is necessarily inelegant, since what you are
doing is nicely explicit in which method you need to apply to the
embedded object. And since the object accessor is different in each
case, it is hard to see what would be more elegant.  Even your example
of CALL-NEXT-METHOD involves having an explicit invocation of what that
method should be.  It isn't clear how having a
CALL-SAME-GENERIC-FUNCTION [1] would really save you anything in either
typing or clarity of presentation.  I suppose the benefit would be if
you changed the name of the function, you wouldn't have to update the
method bodies.

So, I don't really see anything particularly inelegant about being
explicit with the function name.

Now, for your particular example, you could define method on
PRINT-OBJECT, but I suspect you actually have a different real problem
to solve.


-Tom.

[1] I was originally going to name this CALL-SAME-METHOD, but decided
that wasn't really an accurate description, since a different method
would perhaps be called, based on the different argument to the generic
function. 

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Pascal J. Bourguignon
Subject: Re: chaining accessors in generic function
Date: 
Message-ID: <7cwse0bgl8.fsf@pbourguignon.anevia.com>
···@sevak.isi.edu (Thomas A. Russ) writes:

> Tamas K Papp <······@gmail.com> writes:
>
>> Hi,
>> 
>> Suppose I have an object and an associated method.  Then another
>> object which contains exactly one instance of the previous object (+
>> some extra stuff, not presented in the example below), so I can call
>> the same method on it, and so on.  Simplified example:
>> 
>> (defclass a ()
>>   ((text :initform "foo" :accessor text)))
>> 
>> (defclass b ()
>>   ((a :initarg :a :accessor a-of)))
>> 
>> (defclass c ()
>>   ((b :initarg :b :accessor b-of)))
>> 
>> (defgeneric say (object)
>>   (:method ((a a))
>>     (format t "text is ~a" (text a)))
>>   (:method ((b b))
>>     (say (a-of b)))
>>   (:method ((c c))
>>     (say (b-of c))))
>> 
>> (defparameter *a* (make-instance 'a))
>> (say *a*) ;; prints "text is foo"
>> (defparameter *b* (make-instance 'b :a *a*))
>> (say *b*) ;; prints "text is foo"
>> (defparameter *c* (make-instance 'c :b *b*))
>> (say *c*) ;; prints "text is foo"
>> 
>> But this is a bit inelegant, because I have to use "say" explicitly in
>> the defgeneric form above.  Is there a way around this?  I read about
>> call-next-method, but that is for something else.
>
> Well, I don't think it is necessarily inelegant, since what you are
> doing is nicely explicit in which method you need to apply to the
> embedded object. And since the object accessor is different in each
> case, it is hard to see what would be more elegant.  

Indeed.  It looks like a delegate, but it is not.  If it was made into
a delegate pattern, it would be easier:



(defmacro define-delegation (method ((object class) &rest arguments) delegate-slot)
  "Defines a METHOD on CLASS that is implemented as a delegate call 
to the object stored in the DELEGATE-SLOT of the OBJECT."
  `(defmethod ,method ((,object ,class) ,@arguments)
      (,method (slot-value ,object ',delegate-slot)
               ,@(mapcar (lambda (arg) (if (listp arg) (first arg) arg)) arguments))))



(defclass a ()
  ((text :initform "foo" :accessor text)))

(defgeneric do-something (o))
(defmethod do-something ((self a))
   (format t "text is ~A~%" (text self)))

(defgeneric compute-something (o a))
(defmethod compute-something ((self a) arg)
  (* (length (text self)) arg))



(defclass b ()
  ((a :initarg :a :accessor a-of)))

(define-delegation do-something      ((self b))               a)
(define-delegation compute-something ((self b) (arg integer)) a)


(defclass c ()
  ((b :initarg :b :accessor b-of)))

(define-delegation do-something      ((self c))     b)
(define-delegation compute-something ((self c) arg) b)



(defparameter *a* (make-instance 'a))
(defparameter *b* (make-instance 'b :a *a*))
(defparameter *c* (make-instance 'c :b *b*))
(progn
 (terpri)
 (do-something *a*) ;; prints "text is foo"
 (do-something *b*) ;; prints "text is foo"
 (do-something *c*) ;; prints "text is foo"
 (list (compute-something *a* 5) (compute-something *b* 5) (compute-something *c* 5)))

prints:

text is foo
text is foo
text is foo

returns:

(15 15 15)

-- 
__Pascal Bourguignon__