[comp.lang.lisp] CLOS questions

dsm@ctt.bellcore.com (David S. Miller) (10/25/90)

I'm thinking of using CLOS to implement a project, and I have a few
questions about it.  I apologize in advance if any of these questions
have been asked before, or are inappropriate for this newsgroup.  


(1) Are there any public domain (ftp'able) versions of CLOS out there?
(for a Sun 3)?


(2) Is it possible for an object to be an instance of two disjoint classes?

(e.g.) Supposing I have classes representing groups of people such as
employees, students, and athletes.  What if I want to represent an individual
student-employee, or student-athlete, without creating a new class?

As far as I can tell, it doesn't seem possible for an object to be an
instance of more than one class.  Yet, it seems inappropriate to have
to create a new class every time you want a new instance to inherit
attributes from more than one existing class.


(3) Is there any mechanism in CLOS for automatically classifying an
object into a class based on attribute values?

(e.g.) In the previous example, suppose the 'student' class has an
"athletic-prowess' slot.  If the value of that slot was set to 'high
for a particular instance, then I'd want that instance to be
classified into the 'athlete' class as well.

The best way that I can think of to handle this is to define a method
such as:
(defmethod (setf athletic-prowess) ((new-value (eql 'high)) ...)
	...classify this instance as an athlete...  )

Is there a better way to do this?


Thanks in advance for any help on these matters.
I will post summaries of responses e-mailed to me that are of general
interest.

David Miller			|INTERNET:	dsm@ctt.bellcore.com
Bellcore - RRC 1H206		|UUCP:		..!bellcore!ctt!dsm
444 Hoes Lane			|PHONE:		(201) 699-4533
Piscataway, NJ 08854-4182	|








-- 
David S. Miller			|INTERNET:	dsm@ctt.bellcore.com
Bellcore - RRC 1H206		|UUCP:		..!bellcore!ctt!dsm
444 Hoes Lane			|PHONE:		(201) 699-4533
Piscataway, NJ 08854-4182	|

scott@alice.coyote.trw.com (Scott Simpson) (10/25/90)

In article <28175@bellcore.bellcore.com> dsm@ctt.bellcore.com (David S. Miller) writes:
>I'm thinking of using CLOS to implement a project, and I have a few
>(1) Are there any public domain (ftp'able) versions of CLOS out there?
>(for a Sun 3)?

PCL (Portable Common Loops) from arisia.xerox.com. I believe it is
very close to standard CLOS.

>(2) Is it possible for an object to be an instance of two disjoint classes?

Not that I know of.

>(e.g.) Supposing I have classes representing groups of people such as
>employees, students, and athletes.  What if I want to represent an individual
>student-employee, or student-athlete, without creating a new class?

Why don't you want to create a new class? Assuming that employees,
students and athletes are all disjoint, you could have a hierarchy
like

        		 		person
					   |
            ------------------------------------------------------
       	   /				   |		          \
	employee			student		        athlete
           \    			 | \                       / |
	    \   			 |  -----------------------  |
	     \  			 |      	|            |
	      \ 			 |          student-athlete  |
	       ---------------- 	 |      	             |
			        \        |      	             |
				 \ 	 | 		             /
				  \	 | 	          -----------
				student-employee-athlete /

>As far as I can tell, it doesn't seem possible for an object to be an
>instance of more than one class.  Yet, it seems inappropriate to have
>to create a new class every time you want a new instance to inherit
>attributes from more than one existing class.

You can inherit attributes from more than one class by using multiple
inheritance. You still have to create a new class to do multiple
inheritance though. Why is creating a new class inappropriate?

>(3) Is there any mechanism in CLOS for automatically classifying an
>object into a class based on attribute values?
>
>(e.g.) In the previous example, suppose the 'student' class has an
>"athletic-prowess' slot.  If the value of that slot was set to 'high
>for a particular instance, then I'd want that instance to be
>classified into the 'athlete' class as well.

Why couldn't you write a routine to take a parameter that is the
athletic-prowess? You then compute on this parameter and create either
a student or athlete object and return it. Athlete would inherit from
student and student would contain the athletic-prowess slot.

>The best way that I can think of to handle this is to define a method
>such as:
>(defmethod (setf athletic-prowess) ((new-value (eql 'high)) ...)
>	...classify this instance as an athlete...  )

The problem is that you want to reclassify an object after creation
time. The only way I know of doing this is to create a brand new
object in a different class. If you didn't create a brand new object,
then anybody pointing to the old object would suddenly be pointing to
a new object of a different type! Ada has this problem with
dynamically allocated discriminated record types. Consequently, in
Ada, you cannot change the discriminant after the record is
dynamically allocated.
--
Scott Simpson			TRW			scott@coyote.trw.com

halvers@betelgeuse.crd.ge.com (Pete Halverson) (10/25/90)

In article <2726886C.2C1B@wilbur.coyote.trw.com> Scott Simpson writes:
>In article <28175@bellcore.bellcore.com> David S. Miller writes:
>>(3) Is there any mechanism in CLOS for automatically classifying an
>>object into a class based on attribute values?

Not automatically, no.  That kind of functionality hasn't yet trickled down
from the high-end knowledge representation schemes (e.g. KRL, LOOM) to the
more mundane OOP languages.

>>
>>(e.g.) In [a] previous example, suppose the 'student' class has an
>>"athletic-prowess' slot.  If the value of that slot was set to 'high
>>for a particular instance, then I'd want that instance to be
>>classified into the 'athlete' class as well.
>>The best way that I can think of to handle this is to define a method
>>such as:
>>(defmethod (setf athletic-prowess) ((new-value (eql 'high)) ...)
>>	...classify this instance as an athlete...  )
>
>The problem is that you want to reclassify an object after creation
>time. The only way I know of doing this is to create a brand new
>object in a different class. 

The standard method CHANGE-CLASS can be used to mutate an instance into a
different class, calling the extendable generic function
CHANGE-INSTANCE-FOR-DIFFERENT-CLASS The default method removes old slots
and values and adds new ones, leaving any common slots alone.  There
doesn't seem to be any restriction about what classes of instances may be
changed into other classes (as long as they're all standard classes), so
presumably specializing an object class is no problem.

As Scott pointed out, you *do* need to create a combined class that uses
both STUDENT and ATHLETE, so your "classification" method might look
something like

(defmethod (setf athletic-prowess) ((new-value (eql 'high)) (student STUDENT))
  (change-class student 'STUDENT-ATHLETE))           

However, I suppose it might be possible for this method to define the
appropriate combined class *dynamically*, so that it would work for any class
with an ATHLETIC-PROWESS accessor.  The precise incantations for that
relate to the Meta-Object protocol, though, so they're still not fully
defined (and therefore not well supported in existing implementations).

>If you didn't create a brand new object, then anybody pointing to the old
>object would suddenly be pointing to a new object of a different type!

Sounds exactly like what David's asking for---the same object identity, so
that existing pointers remain valid, but a more restricted object type.

>Ada has this problem with dynamically allocated discriminated record types.
>Consequently, in Ada, you cannot change the discriminant after the record
>is dynamically allocated.

As has been pointed out quite recently in this newsgroup, Common Lisp is
not Ada (thank heavens), nor is it Eiffel.  Anyway, it sounds like David
wants a *restriction* on the original type, which, in those languages,
wouldn't necessarily break whatever type assumptions and invariants were
derived from the original instance type.

--
===============================================================================
Pete Halverson                        		    INET: halverson@crd.ge.com 
GE Corporate R&D Center	                      UUCP: uunet!crd.ge.com!halverson
Schenectady, NY                     "Money for nuthin' and your MIPS for free"

john@linus.mitre.org (John D. Burger) (10/26/90)

The reason a new class has to be created when you want to inherit from
two or more classes is that CLOS requires you to define the ORDER of
the inheritance.  Thus if #<Fred> is an instance of both
#<Standard-Class ATHLETE> and #<Standard-Class STUDENT>, it's
necessary to know the order in which #<Fred> should inherit from these
classes.  This must be done in CLOS by defining a NEW class, say
#<Standard-Class STUDENT-ATHLETE>, whose DIRECT-SUPERCLASSES slot
dictates the order of inheritance.

scott@alice.coyote.trw.com (Scott Simpson) writes:

>The problem is that you want to reclassify an object after creation
>time. The only way I know of doing this is to create a brand new
>object in a different class.

Actually, one can use CHANGE-CLASS to ``coerce'' an instance from its
current class to a new one.  Thus

(SETF FRED (MAKE-INSTANCE 'STUDENT :NAME "Fred"))
  #<Instance of STUDENT Fred 1ab3467>
(CLASS-OF FRED)
  #<Standard-Class STUDENT>
(CHANGE-CLASS FRED 'ATHLETE)
  #<Instance of ATHLETE Fred 1ab3467>
(CLASS-OF FRED)
  #<Standard-Class ATHLETE>

Note that Fred is the exact same object.

It's fairly easy to write CLOS code to cons up new mixtures of
existing classes when necessary.  For instance, posit a function
FIND-MIXTURE, which takes any number of classes as arguments, and
finds or creates a new class with exactly those supers.  Thus
(FIND-MIXTURE 'STUDENT 'ATHLETE) would create a new class, perhaps
called STUDENT-ATHLETE, assuming no such class already existed.  Then
one could call CHANGE-CLASS on Fred and this new class, whenever it
became apparent that Fred was not just a student.
-- 
John Burger                                               john@mitre.org

"You ever think about .signature files? I mean, do we really need them?"
  - alt.andy.rooney

jeff@aiai.ed.ac.uk (Jeff Dalton) (10/29/90)

In article <124464@linus.mitre.org> john@mitre.org writes:

>The reason a new class has to be created when you want to inherit from
>two or more classes is that CLOS requires you to define the ORDER of
>the inheritance.

This is true as a description.  CLOS requires that you define a new
class; when you define a class, you have to list the superclasses in
some order; and this order constrains the total class precedence list
for the class being defined.  So, CLOS requires you to define a new
class, and that means specifying the inheritance (to an extent -- you
specify constraints).

But there the new-class requirement explains the define-inheritance
requirement.  We still need an explanation of the new-class requirement
itself.

So: is there an inheritance-definition requirement that is independent
of the requirement that you define a new class?

It's not clear that there is.  CLOS does not require that you 
specify a total order for inheritance.  Class definitions specify
only a partial order; the rest is an artifact of the algorithm used
for building the class precedence list.

The algorithm is specified so that the total order is always defined.
It could have been otherwise: for example, CLOS implementations might
have been allowed to choose any total order consistent with the
partial one.  So presumably the CLOS designers chose to make things
more defined rather than less.

However, if you could specify more than one class to MAKE-INSTANCE,
those classes would be in some order, and that order could be
interpreted in the same way as the order in a DEFCLASS.  Inheritance
would be no less defined than it was before.

On the other hand, the class precedence list would then be a property
of an instance rather than a class, at least in some cases, which
would make CLOS a different kind of object system than it is.  It
would be necessary to rething many aspect of the design, or at least
make sure they still made sense.

So you have to define a new class.  But you could define a
MAKE-MULTI-INSTANCE that constructed a special-purpose class
and then made an instance of it, so you can get something
fairly close to this part of what you want.