From: Tamas Papp
Subject: CLOS, copy & modify
Date: 
Message-ID: <87tzqn4ray.fsf@pu100877.student.princeton.edu>
I am using CLOS to manage drawing parameters for my plotting code.
Example:

(defclass dash-style ()  
  ((offset :initform 0d0 :initarg :offset :type double-float)
   (dashes :initform #() :initarg :dashes)))
   
(defclass line-style ()
  ((width :initform 1 :initarg :width :accessor width)
   (color :initform +black+ :initarg :color)
   (dash-style :initform (make-instance 'dash-style) :initarg :dash-style)))

(defparameter *default-line-style* (make-instance 'line-style))

(defparameter *dash-dash* (make-instance 'dash-style :dashes #(4 4)))
(defparameter *dash-dot* (make-instance 'dash-style :dashes #(1 1)))

Nesting goes on, a plot-style has an axis-style, which has a
line-style, with a dash-style.  I call functions of my library like

(simple-2d-plot canvas x y plot-style)

I have created *default-something-style* instances.  I would like to
have functions which 

1. copy a given style, recursively

Eg

(defmethod copy-style ((style line-style))
  (with-slots (width color dash-style)
     (make-instance 'line-style :width (copy-style width)
                                :color (copy-style color)
                                :dash-stype (copy-style dash-style))))

Recursion would stop at the copying of atoms, eg

(defmethod copy-style ((style real))
   style)

I tried writing macros which would generate the first copy-style
method from something like 

(define-copy-style line-style width color dash-style)

but couldn't get the keywords (:width, etc).


2. modify the sub-..-sub-slot of an object

Eg 

(defparameter *my-line-style* (copy-style *default-line-style*)
(set-substyle *my-line-style* '(dash-style offset) 2)

where the latter would have the same effect as

(setf (slot-value (slot-value *my-line-style* 'dash-style) 'offset) 2)

I thought of a macro, but it would be more useful to be able to
specify lists that identify a substyle at runtime, but I don't know
how to do this in a function.

I wonder if the above code is the right approach.  I would like to
know the solutions for my questions, but suggestions on how to do this
in other ways are welcome too.

Tamas

From: Pascal Costanza
Subject: Re: CLOS, copy & modify
Date: 
Message-ID: <5jatm7F3s8d42U1@mid.individual.net>
Tamas Papp wrote:

> I wonder if the above code is the right approach.  I would like to
> know the solutions for my questions, but suggestions on how to do this
> in other ways are welcome too.

There are essentially two ways to get what you want:

1) Define your own defclass macro (like define-plotter-class). That 
macro sees all the parameters to the underlying defclass form, so you 
can create additional methods for copying and accessing, etc. You can 
even ask for more keyword arguments to support even more functionality.

2) Implement generic copy and access routines by way of the CLOS MOP. 
For example, the following is a rough version of a generic copy function:

(defun copy-instance (instance)
   (let* ((class (class-of instance))
          (slots (class-slots class))
          (new-instance (make-instance class)))
     (loop for slot in slots do
           (setf (slot-value new-instance (slot-definition-name slot))
                 (slot-value instance (slot-definition-name slot))))
     new-instance))

There are some caveats here:

- This only works for object graphs without cycles.

- This only works if default initialization of instances of the 
respective classes doesn't interfere in anyway with the manual 
initialization in the copy-instance function. It may be necessary to 
replace make-instance with allocate-instance, or so.

Unfortunately, copying objects is not straightforward and cannot be 
generalized well.

But I hope you get the general idea. In a similar way, you could define 
a path expression language for accessing slots deep down in an object 
graph. You may want to look at Adaptive Programming for some ideas here.



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: Michael Weber
Subject: Re: CLOS, copy & modify
Date: 
Message-ID: <1188068460.400170.60970@x40g2000prg.googlegroups.com>
On Aug 25, 4:46 pm, Pascal Costanza <ยทยทยทยท@p-cos.net> wrote:

> - This only works for object graphs without cycles.
>
> - This only works if default initialization of instances of the
> respective classes doesn't interfere in anyway with the manual
> initialization in the copy-instance function. It may be necessary to
> replace make-instance with allocate-instance, or so.
>
> Unfortunately, copying objects is not straightforward and cannot be
> generalized well.

Agreed, of course.  Kent Pitman's article on EQUALity has the details.

Nevertheless, it has been done, even for cyclic structures, IIRC :)

http://www-cgi.cs.cmu.edu/afs/cs/project/ai-repository/ai/lang/lisp/code/ext/copy_obj/

The code is slightly dated and may need some tweaking, though.


Cheers,
M/