Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How make an abstract class in CLOS?

542 views
Skip to first unread message

Erik Naggum

unread,
May 23, 2000, 3:00:00 AM5/23/00
to
* "Sergio" <serg...@teleline.es>
| How can i make an abstract class using defclass?

What's an abstract class?

#:Erik
--
If this is not what you expected, please alter your expectations.

Friedrich Dominicus

unread,
May 23, 2000, 3:00:00 AM5/23/00
to
Erik Naggum <er...@naggum.no> writes:

> * "Sergio" <serg...@teleline.es>
> | How can i make an abstract class using defclass?
>
> What's an abstract class?

It's an class which one can see as a skeleton, but which can not be
instantiated, so descendants are expected to implement the abstract
features. I guess such a thing does not make sense in Common Lisp
but it plays a role e.g in Eiffel, Sather, C++ as a design tool.

Regards
Friedrich

Andrew Cooke

unread,
May 23, 2000, 3:00:00 AM5/23/00
to

The machine I have the ANSI spec on isn't running at the moment, so I
may have the wrong names here, but perhaps you could consider using
defgeneric as the equivalent in some way of an abstract class, and
defmethod as implementing that class? (I'm not sure it's particularly
useful to do so, however.)

Andrew
http://www.andrewcooke.free-online.co.uk/index.html

In article <87zophe...@frown.inka.de>,


Sent via Deja.com http://www.deja.com/
Before you buy.

Bjørn Remseth

unread,
May 23, 2000, 3:00:00 AM5/23/00
to
Erik Naggum <er...@naggum.no> writes:

>
> * "Sergio" <serg...@teleline.es>
> | How can i make an abstract class using defclass?
>
> What's an abstract class?

A class you won't instantiate but which you can (and usually will)
make subclasses of.

But to answer Sergio's question: You simply "defclass" it
like any other class, that's all there is to it ;)


--
(Rmz)

Bj\o rn Remseth !Institutt for Informatikk !Net: r...@ifi.uio.no
Phone:+47 91341332!Universitetet i Oslo, Norway !ICBM: N595625E104337

Anders Vinjar

unread,
May 23, 2000, 3:00:00 AM5/23/00
to
>>> "S" == Sergio <serg...@teleline.es> writes:
S> method in each class. BUT I MUST FORBID make instances of
S> the superclase, because they are "incomplete"
S> objects.

Lots of ways to do this. You might want to specialize a :before
method for "initialize-instance" for the superclass in question.

Sergio

unread,
May 23, 2000, 3:00:00 AM5/23/00
to
Thanks!!.
Well, i'm still thinking at C++ object system when i make CLOS algoritms.
CLOS, with it's generic functions seems to be different that C++ with
sending messages method, with methods encapsulated in class.

coming back to my question, when i asked for abstract class...
I have an OMT model with a superclase and several subclass. The subclass
share identical metods ( thay are in the superclass ) but implement a
specific method in each class. BUT I MUST FORBID make instances of the
superclase, because they are "incomplete" objects. Well, my brain makes
smoke when i translate from spanish to english so ... i hope you understand
me :(((

BYE.
Sergio escribió en mensaje ...


>How can i make an abstract class using defclass?
>

>THANKS.
>
>
>

Barry Margolin

unread,
May 23, 2000, 3:00:00 AM5/23/00
to
In article <m33dn89...@alum.mit.edu>,
David Bakhash <ca...@alum.mit.edu> wrote:
>the real question here is "why do you think you need an abstract class
>in common lisp, and what do you think it would get you?"

It's interesting to note that Flavors, one of the two main influences on
the design of CLOS, *did* have the notion of abstract classes. DEFFLAVOR
had an :ABSTRACT-FLAVOR option that indicated that the flavor couldn't be
instantiated by itself. It also had :REQUIRED-INSTANCE-VARIABLES and
:REQUIRED-METHODS, to declare that a flavor incorporating this flavor as a
base has to define these instance variables (what CLOS calls slots) and
methods, since methods in the base flavor will reference them
(:REQUIRED-METHODS is analogous to C++'s technique where the base class
declares a method with '= 0'). These things allow the base class to define
a protocol that mixins are supposed to implement.

--
Barry Margolin, bar...@genuity.net
Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.

Erik Naggum

unread,
May 23, 2000, 3:00:00 AM5/23/00
to
* Friedrich Dominicus <Friedrich...@inka.de>

| It's an class which one can see as a skeleton, but which can not be
| instantiated, so descendants are expected to implement the abstract
| features. I guess such a thing does not make sense in Common Lisp
| but it plays a role e.g in Eiffel, Sather, C++ as a design tool.

Let's see is the person requesting this can describe it well enough
to be implementable or even desirable in Common Lisp. I'm not too
interested in what it would mean in another language, but this is
probably because I occasionally use English expressions in Norwegian
and vice versa and thus know just how incredibly embarrassing it can
be to forget which language one is using.

Frode Vatvedt Fjeld

unread,
May 23, 2000, 3:00:00 AM5/23/00
to
"Sergio" <serg...@teleline.es> writes:

> The subclass share identical metods ( thay are in the superclass )
> but implement a specific method in each class. BUT I MUST FORBID
> make instances of the superclase, because they are "incomplete"
> objects.

Lisp programmers tend to have faith in themselves not to make
instances of classes unless they have a reason to. So my first answer
would be "just don't make instances of it". If you really want to
prevent it, I guess you could do something like this:

(defmethod make-instance ((class abstract-class) &rest ignore)
(warn "Someone tried to make an instance of abstract-class!")
nil)

--
Frode Vatvedt Fjeld

Bjørn Remseth

unread,
May 23, 2000, 3:00:00 AM5/23/00
to

Frode Vatvedt Fjeld <fro...@acm.org> writes:

> Lisp programmers tend to have faith in themselves not to make
> instances of classes unless they have a reason to. So my first answer
> would be "just don't make instances of it". If you really want to
> prevent it, I guess you could do something like this:
>
> (defmethod make-instance ((class abstract-class) &rest ignore)
> (warn "Someone tried to make an instance of abstract-class!")
> nil)

... or you could let some MOP hackery to do it for you, but as you
say, it really is unecessary.

Hartmann Schaffer

unread,
May 23, 2000, 3:00:00 AM5/23/00
to
In article <lTwW4.1683$Qk4....@telenews.teleline.es>,

"Sergio" <serg...@teleline.es> writes:
> Thanks!!.
> Well, i'm still thinking at C++ object system when i make CLOS algoritms.
> CLOS, with it's generic functions seems to be different that C++ with
> sending messages method, with methods encapsulated in class.
>
> coming back to my question, when i asked for abstract class...
> I have an OMT model with a superclase and several subclass. The subclass

> share identical metods ( thay are in the superclass ) but implement a
> specific method in each class. BUT I MUST FORBID make instances of the
> superclase, because they are "incomplete" objects. Well, my brain makes

how about defining a meke method rhat raises an error (i am sure that is
possible, but my referennce is on a machine that currently is down))

> smoke when i translate from spanish to english so ... i hope you understand
> me :(((


--

Hartmann Schaffer


Phil Stubblefield

unread,
May 23, 2000, 3:00:00 AM5/23/00
to
Sergio <serg...@teleline.es> wrote:
>
> How can i make an abstract class using defclass?

Other posters have already addressed some of the issues, specifically
that CLOS does not support the definition of abstract classes in the
same way that Java does (unless, of course, you use the MOP to define
the behavior yourself). For example, it takes more than a single
keyword to enforce the desire that all subclasses of a given class
implement a specific method. In fact, as David pointed out, CLOS
methods do not "belong" to a class in the same way that Java or C++
methods do, so you must adjust your thinking accordingly.

However, you can certainly communicate the *intent* of an abstract
class, which can be just as important as enforcing compile-time
behavior. For example, suppose you want to defined an abstract class
named SHAPE, each of whose subclasses must implement the methods AREA
and PERIMETER. The following code is fairly simple, but effectively
communicates the author's intent to the reader. It does not enforce
compile-time behavior, but will signal a meaningful run-time error if
a required method is absent from a particular subclass.

--------------------------------- snip ---------------------------------

(in-package "CL-USER")


;;; The abstract class SHAPE.

(defclass shape ()
()
(:documentation "An abstract shape.

This class is not intended to be instantiated."))

(defun make-shape (class &rest initargs)
"Creates and returns a shape having the given characteristics."
;; Could add type-checking code here....
(apply #'make-instance class initargs))

(defgeneric area (shape)
(:documentation "Returns the area of the given shape.")
(:method ((shape shape))
"This default method signals an error."
(error "The required method 'AREA' is missing from ~
the class '~S'."
(class-name (class-of shape)))))

(defgeneric perimeter (shape)
(:documentation "Returns the perimeter of the given shape.")
(:method ((shape shape))
"This default method signals an error."
(error "The required method 'PERIMETER' is missing from ~
the class '~S'."
(class-name (class-of shape)))))


;;; The concrete subclass SQUARE.
;;;
;;; Note that this class defines the required methods by hand.

(defclass square (shape)
((side
:reader side
:initarg :side
:documentation "The length of one side of this square.")))

(defmethod area ((square square))
"Returns the area of the given square."
(let ((side (side square)))
(* side side)))

(defmethod perimeter ((square square))
"Returns the perimeter of the given square."
(* 4 (side square)))


;;; The concrete subclass CIRCLE.
;;;
;;; Note that this class defines the required methods using slot
;;; readers.

(defclass circle (shape)
((radius
:reader radius
:initarg :radius
:documentation "The length of the radius of this circle.")
(area
:reader area
:documentation "The area of this circle.")
(perimeter
:reader perimeter
:documentation "The perimeter of this circle.")))

(defmethod initialize-instance :after ((circle circle) &key)
(with-slots (radius area perimeter) circle
(setq area (* pi radius radius))
(setq perimeter (* 2 pi radius))))

--------------------------------- snip ---------------------------------

Using these definitions:

USER(13): (setq s (make-shape 'square :side 1))
#<SQUARE @ #x225d298a>
USER(14): (area s)
1
USER(15): (perimeter s)
4
USER(16): (setq c (make-shape 'circle :radius 1))
#<CIRCLE @ #x225d3682>
USER(17): (area c)
3.141592653589793
USER(18): (perimeter c)
6.283185307179586


Some appropriate macros and utility functions could clean up this
example and prevent unnecessary duplication of code so that you
could, for example, write

(define-abstract-method area (shape))

which would expand into a DEFGENERIC form as shown above. In fact, you
could take this idea to its logical conclusion by defining a macro that
allows you to write

(define-abstract-class shape ()
(:required-method area (shape))
(:required-method perimeter (shape)))

and have it expand into a PROGN containing the appropriate forms.

However, please permit me to make an observation based on personal
experience. If you have the free time, then by all means take the
time to define DEFINE-ABSTRACT-METHOD and DEFINE-ABSTRACT-CLASS,
expanding their capabilities as necessary to meet your needs. You'll
probably learn some invaluable lessons about macros and about CLOS.
Then, after you've defined the macros to your satisfaction -- consider
throwing most of your work away!

Why would you do that? Well, before maintaining or enhancing your
code, the next programmer who comes along must first understand it.
If your code uses your own high-level abstractions, then such a
programmer must digest those definitions *before* even beginning to
understand your domain-specific code. For example, most readers will
understand the general intent of DEFINE-ABSTRACT-CLASS, but unless
you've put a lot of thought into the issues, specific behaviors may be
very unexpected. For example, can an abstract class also define
normal slots? What happens when one abstract class inherits from
another? What if a third class inherits from two different abstract
classes, each of which defines a required method having the same name
but different lambda lists? And so on, ad infinitum. If you as the
programmer use DEFCLASS, DEFGENERIC, and DEFMETHOD forms, then I can
usually decipher these interactions without undue effort. But if you
use DEFINE-ABSTRACT-CLASS, then I must understand your implementation
in some detail before modifying your code.

OTOH, high-level abstractions often encapsulate difficult pieces of
code and help to reduce clutter. As a recent poster said in another
thread, they can hide the crufty details in order to emphasize the
structure of the problem at hand. You as the author must decide when
and where to use them. But always keep in mind the reader of your
code -- which may be yourself several months or years from now!


Phil Stubblefield
Rockwell Palo Alto Laboratory 206/655-3204
http://www.rpal.rockwell.com/~phil ph...@rpal.rockwell.com

Tim Bradshaw

unread,
May 23, 2000, 3:00:00 AM5/23/00
to
* Frode Vatvedt Fjeld wrote:
> Lisp programmers tend to have faith in themselves not to make
> instances of classes unless they have a reason to. So my first answer
> would be "just don't make instances of it". If you really want to
> prevent it, I guess you could do something like this:

> (defmethod make-instance ((class abstract-class) &rest ignore)
> (warn "Someone tried to make an instance of abstract-class!")
> nil)

You may be being more sophisticated than I'm assuming, but this won't
work unless you do MOPpery, because MAKE-INSTANCE is called with an
object which is the class of the object you want to make, and the
class of *that* object will be STANDARD-CLASS.

You can get around this without the MOP by having EQL methods for
MAKE-INSTANCE, but that's pretty horrid.

With the (or a) MOP it becomes very easy:

(defclass abstract-class (standard-class)
())

(defmethod make-instance ((class abstract-class) &rest junk)
(declare (ignore junk))
(error "Making an instance of an abstract class"))


(defclass abstract-super ()
(...)
(:metaclass abstract-class))

(defclass concrete-sub (abstract-super)
(...))

or something like that should work.

--tim

Erik Naggum

unread,
May 23, 2000, 3:00:00 AM5/23/00
to
* Andrew Cooke

| The machine I have the ANSI spec on isn't running at the moment, so
| I may have the wrong names here, but perhaps you could consider
| using defgeneric as the equivalent in some way of an abstract class,
| and defmethod as implementing that class? (I'm not sure it's
| particularly useful to do so, however.)

On the contrary, this is really what we're looking for. In my view,
this is the only intelligent transposition of the "abstract class"
bullshit from the C++ world into areal object-oriented system, and
it shows that a fairly simple concept (generic functions) needs to
be reimplemented in a round-about, counter-intuitive way in an "OO"
system that fails to understand that classes do not "own" methods.

"Abstract classes" do not have anything to do with the _classes_.
It's a misfeature of the encapsulation mechanism in C++ classes,
which is itself a misfeature of its implementation specifics and
its legacy from C and a misunderstanding of Simula's mechanisms.

It was said back in the early 1990's data communication that the
question "What is X.25?" could be asked by only ignorants and deeply
sarcastic experts. Later, "What is a database?" and "What is
object-orientation?" have become similar questions. I thought it
was appropriate to ask "What is an abstrat class?" the same way.

Tim Moore

unread,
May 23, 2000, 3:00:00 AM5/23/00
to
On 23 May 2000, Tim Bradshaw wrote:

> You can get around this without the MOP by having EQL methods for
> MAKE-INSTANCE, but that's pretty horrid.
>

How about a primary method on initialize-instance or shared-initialize?

(defmethod initialize-instance ((class abstract-class) &rest junk)
(declare (ignore junk))
(error "Someone tried to make an instance of abstract-class!"))

Tim


Tim Bradshaw

unread,
May 23, 2000, 3:00:00 AM5/23/00
to
* Frode Vatvedt Fjeld wrote:
> Tim Bradshaw <t...@cley.com> writes:
>> You can get around this without the MOP by having EQL methods for
>> MAKE-INSTANCE, but that's pretty horrid.

> Yes, I made a slight mistake, but I don't really think eql
> specialization is so bad:

> (defmethod make-instance ((class (eql (find-class abstract-class))) &rest ignore)
> (warn "Don't do this.")
> nil)

It probably is bad in that it's likely to make MAKE-INSTANCE be slower
for all calls, and that may be on all sorts of critical paths.

(I'm not sure enough about implementation techniques here to be sure
it really does make it slow, but I've certainly heard it claimed that
it may.)

Anyway, the MOPpy solution is kind of easy enough that it's tempting
to use that on systems with enough MOP.

Tim Bradshaw

unread,
May 23, 2000, 3:00:00 AM5/23/00
to
* Tim Moore wrote:
> On 23 May 2000, Tim Bradshaw wrote:
>> You can get around this without the MOP by having EQL methods for
>> MAKE-INSTANCE, but that's pretty horrid.
>>

> How about a primary method on initialize-instance or shared-initialize?

> (defmethod initialize-instance ((class abstract-class) &rest junk)
> (declare (ignore junk))
> (error "Someone tried to make an instance of abstract-class!"))

This makes all subclasses of ABSTRACT-CLASS noninstantiable, which is
unlikely to be what you want.

--tim


David Hanley

unread,
May 23, 2000, 3:00:00 AM5/23/00
to

Sergio wrote:

> How can i make an abstract class using defclass?

SInce CLOS uses a fundamentally different model than C++
this may not be a useful question; specifically, you do not need
to make a base class to do the following:

(defclass square () ( .. ))
(defclass circle () ( .. ))

(defmethod area( (s square ) ) ( .. ) )
(defmethod area( (c circle ) ) ( .. ) )

(defun foo( shape )( .... (area shape ) ) )

In C++ "foo" would have to take an ancestor of both square and
circle. In CLOS this is not necessary, so abstract classes are
somewhat superfluous.

People have already shown how to do what you are specifically
asking for in lisp, but i thought this might still be of help.

dave


David Bakhash

unread,
May 24, 2000, 3:00:00 AM5/24/00
to
the real question here is "why do you think you need an abstract class
in common lisp, and what do you think it would get you?"

I programmed in CLOS for a long time before I ever considered this
abtract class notion. I think the better thing to do with CLOS is to
literally forget what you know from other object systems (especially
Java, which only confused me, and troubled me in its limitations).
don't worry about finding analogous terms and constructs to what you
already know. The reason is that while some points in CLOS are indeed
subtle, and some are complicated, and there are several steps one must
know about, CLOS on a whole is not too bad. you can do most things
quite easily. In fact, the macro DEFCLASS does so much, surprisingly,
that you hardly have to think.

If you get caught trying to find rigorous analogies between CLOS and
(say) Java's object system, then you'll be troubled because in Java
(and C++), methods belong (and I think are lexically defined) inside
the classes to which they apply. Since methods belong to a paticular
class, they can't dispatch based on multiple classes (at least not
without ugly CASE-like statments or something). Furthermore, Java's
single-inheritance-plus-interfaces hack is different from CLOS's
multiple inheritance. then, with method combination and other things,
you just start to realize that it's best to drop what you know and try
learning CLOS with an open mind. Then I think that the most important
90% or so is fairly easy.

dave

Frode Vatvedt Fjeld

unread,
May 24, 2000, 3:00:00 AM5/24/00
to
Tim Bradshaw <t...@cley.com> writes:

> You can get around this without the MOP by having EQL methods for
> MAKE-INSTANCE, but that's pretty horrid.

Yes, I made a slight mistake, but I don't really think eql
specialization is so bad:

(defmethod make-instance ((class (eql (find-class abstract-class))) &rest ignore)
(warn "Don't do this.")
nil)


It also occurred to me that the equivalent of an abstract class in CL
would be a generic function for which there is no applicable method
(given some particular arguments). The need to "forbid" this in other
languages can maybe partly be attributed to the fact that they often
don't have run-time error handling (conditions), hence the problem
_must_ be tackled at compile-time, if at all.

--
Frode Vatvedt Fjeld

Tim Moore

unread,
May 24, 2000, 3:00:00 AM5/24/00
to
On 23 May 2000, Tim Bradshaw wrote:

> * Tim Moore wrote:
> > How about a primary method on initialize-instance or shared-initialize?
>
> > (defmethod initialize-instance ((class abstract-class) &rest junk)
> > (declare (ignore junk))
> > (error "Someone tried to make an instance of abstract-class!"))
>
> This makes all subclasses of ABSTRACT-CLASS noninstantiable, which is
> unlikely to be what you want.

Damn, you're right. Somehow I thought that a new initialize-instance
method got defined for each class, but on further reflection that couldn't
be right. Shows what I know.

Tim

Espen Vestre

unread,
May 24, 2000, 3:00:00 AM5/24/00
to
Friedrich Dominicus <Friedrich...@inka.de> writes:

> It's an class which one can see as a skeleton, but which can not be
> instantiated, so descendants are expected to implement the abstract
> features. I guess such a thing does not make sense in Common Lisp
> but it plays a role e.g in Eiffel, Sather, C++ as a design tool.

You don't want to do this (why do I always get the feeling that
the hardcore OO camp is living in a different dimension than I?),
but here are a few statements that indicate that you can put
such handcuffs on you if you use the mop:

USER(19): (defclass abstract (standard-class)
())
#<STANDARD-CLASS ABSTRACT>
USER(20): (defclass dummy ()
()
(:metaclass abstract))
#<ABSTRACT DUMMY>
USER(21): (defmethod make-instance ((x abstract) &rest initargs)
(error "You're not allowed to make me!"))
#<STANDARD-METHOD MAKE-INSTANCE (ABSTRACT)>
USER(22): (defclass foo (dummy)
())
#<STANDARD-CLASS FOO>
USER(23): (defmethod gobble ((x dummy))
(format t "Gobble-gobble!~%"))
#<STANDARD-METHOD GOBBLE (DUMMY)>
USER(24): (make-instance 'dummy)
Error: You're not allowed to make me!

Restart actions (select using :continue):
0: Return to Top Level (an "abort" restart)
1: Abort #<PROCESS Initial Lisp Listener>
[1] USER(25): :pop
USER(26): (make-instance 'foo)
#<FOO @ #x46ce682>
USER(27): (gobble *)
Gobble-gobble!
NIL
USER(28):
--
(espen)

Espen Vestre

unread,
May 24, 2000, 3:00:00 AM5/24/00
to
Espen Vestre <espen@*do-not-spam-me*.vestre.net> writes:

> You don't want to do this (why do I always get the feeling that
> the hardcore OO camp is living in a different dimension than I?),
> but here are a few statements that indicate that you can put
> such handcuffs on you if you use the mop:

whoops, seems like Tim Bradshaw had already made an almost identical
posting that I missed at first sight - sorry about that...
--
(espen)

Frode Vatvedt Fjeld

unread,
May 24, 2000, 3:00:00 AM5/24/00
to
Tim Bradshaw <t...@cley.com> writes:

> It probably is bad in that it's likely to make MAKE-INSTANCE be slower
> for all calls, and that may be on all sorts of critical paths.

> [...]


> Anyway, the MOPpy solution is kind of easy enough that it's tempting
> to use that on systems with enough MOP.

Well, when you give an example and say "don't do this", subtle points
about performance are not really a concern. That said, I'd guess that
EQL-specialization is much faster than class-specialization, since it
involves a single pointer comparison (I'm still guessing) rather than
looking up some class hierarchy.

--
Frode Vatvedt Fjeld

Tim Bradshaw

unread,
May 24, 2000, 3:00:00 AM5/24/00
to
* Frode Vatvedt Fjeld wrote:

> Well, when you give an example and say "don't do this", subtle points
> about performance are not really a concern.

Yes, I'm sorry if I implied anything I didn't mean to.

> That said, I'd guess that
> EQL-specialization is much faster than class-specialization, since it
> involves a single pointer comparison (I'm still guessing) rather than
> looking up some class hierarchy.

I'm not sure, I'd actually like to know this. My intuition is that
it's slower, but I think that `intuition' may be the result of people
*saying* it's slower rather than any actual feeling for how CLOS
works, and that in turn may be because it was slow in PCL or
something. If any implementors read this I'd like to know the real
story for a modern CLOS implementation.

I tried it on ACL (MAKE-INSTANCE with an EQL method never used vs
MAKE-INSTANCE with a method specialised on a metaclass which is never
used), and I can't measure any difference -- the time is probably
dominated by all the consing and initialisation rather than dispatch.
So as far as that goes, I was wrong.

--tim

Raymond Toy

unread,
May 24, 2000, 3:00:00 AM5/24/00
to
>>>>> "Tim" == Tim Moore <mo...@herschel.bricoworks.com> writes:

Tim> On 23 May 2000, Tim Bradshaw wrote:
>> * Tim Moore wrote:
>> > How about a primary method on initialize-instance or shared-initialize?
>>
>> > (defmethod initialize-instance ((class abstract-class) &rest junk)
>> > (declare (ignore junk))
>> > (error "Someone tried to make an instance of abstract-class!"))
>>
>> This makes all subclasses of ABSTRACT-CLASS noninstantiable, which is
>> unlikely to be what you want.

Tim> Damn, you're right. Somehow I thought that a new initialize-instance
Tim> method got defined for each class, but on further reflection that couldn't
Tim> be right. Shows what I know.

Now I'm confused. Why wouldn't an initialize-instance specialized to
a sub-class of abstract-class do the right thing? Only when there is
no initialize-instance for the sub-class would the method for
abstract-class be called and produce an error right?

Ray

Tim Bradshaw

unread,
May 24, 2000, 3:00:00 AM5/24/00
to

Yes, but you can't write this method-of-the-subclass, because you
don't know what it should do. The default primary method for
INITIALIZE-INSTANCE is system-supplied, and although there's
information about what it does (call SHARED-INITIALIZE), it's not
clear to me that that is *all* it does. The obvious approach to
overriding the default primary method would be something that at some
point does (CALL-NEXT-METHOD) to make sure the default behaviour
happens (or just define an :AFTER method in the normal way...), but
this won't work in this case because you have to hop over (several
levels of) superclass to actually get at the method you want to call.

Actually, I think I'm wrong about all this -- hyperspec Section 7.1.7
seems to mean that it is the case that all INITIALIZE-INSTANCE does is
call SHARED-INITIALIZE, so you could define your own methods for it.
(I guess I should elide all the previous para now...).

It would still be a bit clunky I think as you'd either have to do some
cleverness with mixing classes (define a method signalling an error on
ABSTRACT-CLASS, and one that does the default thing on
NON-ABSTRACT-CLASS, then you can define non-abstract subclasses by
ensuring NON-ABSTRACT-CLASS ends up ahead of ABSTRACT-CLASS in the
CPL:

(defclass noninstantiable (... abstract-class ...) ...)
(defclass instantiable (... non-abstract-class
... noninstantiable ...) ...)

) or define lots of methods for INITIALIZE-INSTANCE for all the
subclasses.

--tim

Marco Antoniotti

unread,
May 24, 2000, 3:00:00 AM5/24/00
to

Barry Margolin <bar...@genuity.net> writes:

> In article <m33dn89...@alum.mit.edu>,
> David Bakhash <ca...@alum.mit.edu> wrote:

> >the real question here is "why do you think you need an abstract class
> >in common lisp, and what do you think it would get you?"
>

> It's interesting to note that Flavors, one of the two main influences on
> the design of CLOS, *did* have the notion of abstract classes. DEFFLAVOR
> had an :ABSTRACT-FLAVOR option that indicated that the flavor couldn't be
> instantiated by itself. It also had :REQUIRED-INSTANCE-VARIABLES and
> :REQUIRED-METHODS, to declare that a flavor incorporating this flavor as a
> base has to define these instance variables (what CLOS calls slots) and
> methods, since methods in the base flavor will reference them
> (:REQUIRED-METHODS is analogous to C++'s technique where the base class
> declares a method with '= 0'). These things allow the base class to define
> a protocol that mixins are supposed to implement.

(defclass an-abstract-class ()
()
(:abstract-p t))

(defclass I-cannot-be-subclassed ()
()
(:sealed t))

any taker?

Cheers

--
Marco Antoniotti ===========================================

Marco Antoniotti

unread,
May 24, 2000, 3:00:00 AM5/24/00
to

Espen Vestre <espen@*do-not-spam-me*.vestre.net> writes:

Do you think this is enough? :{ Just wait for the objection "but
this is a runtime and not a compile-time error!" :{

Jeff Dalton

unread,
May 24, 2000, 3:00:00 AM5/24/00
to
David Bakhash <ca...@alum.mit.edu> writes:

> If you get caught trying to find rigorous analogies between CLOS and
> (say) Java's object system, then you'll be troubled because in Java
> (and C++), methods belong (and I think are lexically defined) inside
> the classes to which they apply. Since methods belong to a paticular
> class, they can't dispatch based on multiple classes (at least not
> without ugly CASE-like statments or something).

Yes, in Java the methods are textually inside the class definitions.

Nonetheless, the existing Java syntax would work just fine for
multi-arg dispatch.

(Indeed, if you know about the multi-arg dispatch, you'll find that
some of the descriptions of how method lookup works in Java could
remain unchanged. Because the authors never read what they wrote from
the point of view of someone who might think all args were treated
dynamically, they failed to see that their words did not make it clear
that dynamic lookup *wasn't* what happened.)

> ... it's best to drop what you know and try learning CLOS with
> an open mind. ...

I agree with you there.

-- jeff


Jeff Dalton

unread,
May 24, 2000, 3:00:00 AM5/24/00
to
Tim Bradshaw <t...@cley.com> writes:

> * Frode Vatvedt Fjeld wrote:

> > ... I'd guess that


> > EQL-specialization is much faster than class-specialization, since it
> > involves a single pointer comparison (I'm still guessing) rather than
> > looking up some class hierarchy.

Any reasonable CLOS implementation would avoid any time-consuming
lookup in the class hierarchy, at least most of the time. I'm not
sure how sophisticated CLOS implementations get, but I'd expect
them (a) to cache the results of any slow method lookup and (b) to
use "special" dispatchers that "knew" something about what methods
had been defined.

So for instance if all the specialisation was EQL, a dispatcher that
handled that case efficiently could be installed.

Remember that for EQL-specialization, there might be different methods
for different objects, just as their might be different methods for
different classes. The chief difference is that to find the method
based on the class of the argument, you have to extract the class of
the argument, which could be as simple as one memory reference.
(Even that might be avoided if the generic function thought it
was going to see the same argument value often enough, since it
could associate the method with the object rather than its class,
rather as if the method had been EQL-specialised.)

> I'm not sure, I'd actually like to know this.

So would I. My guess is that less effort has been put into optimising
the cases that involve EQL-specialisation.

-- jeff

Dan L. Pierson

unread,
May 24, 2000, 3:00:00 AM5/24/00
to
Erik Naggum <er...@naggum.no> wrote:
> On the contrary, this is really what we're looking for. In my view,
> this is the only intelligent transposition of the "abstract class"
> bullshit from the C++ world into areal object-oriented system, and
> it shows that a fairly simple concept (generic functions) needs to
> be reimplemented in a round-about, counter-intuitive way in an "OO"
> system that fails to understand that classes do not "own" methods.
>
> "Abstract classes" do not have anything to do with the _classes_.
> It's a misfeature of the encapsulation mechanism in C++ classes,
> which is itself a misfeature of its implementation specifics and
> its legacy from C and a misunderstanding of Simula's mechanisms.

Close, but not quite. An "abstract class" is closer to a protocol,
i.e. a set of cooperating methods, than it is to a single method.
This means that you'd need to define either a set of generic functions
or my-abstract-class-protocol generic function that returns a defined
set of functions.

Dan Pierson, Control Technology Corporation
d...@control.com

Erik Naggum

unread,
May 24, 2000, 3:00:00 AM5/24/00
to
* Dan L. Pierson <d...@control.com>

| Close, but not quite. An "abstract class" is closer to a protocol,
| i.e. a set of cooperating methods, than it is to a single method.

Huh? Obviously, you need more than one generic function if the
abstract class defines more than one function that has to be
implemented by the real classes. I'm not sure what you're arguing
against, or possibly why you think this is non-obvious.

Barry Margolin

unread,
May 24, 2000, 3:00:00 AM5/24/00
to
In article <31681700...@naggum.no>, Erik Naggum <er...@naggum.no> wrote:
>* Dan L. Pierson <d...@control.com>
>| Close, but not quite. An "abstract class" is closer to a protocol,
>| i.e. a set of cooperating methods, than it is to a single method.
>
> Huh? Obviously, you need more than one generic function if the
> abstract class defines more than one function that has to be
> implemented by the real classes. I'm not sure what you're arguing
> against, or possibly why you think this is non-obvious.

I'm not sure what you're arguing against, either.

Basically, in languages that have them, abstract classes are a way of
encoding some rules about the protocol that the class hierarchy is intended
to implement within the class definitions. In C++, you create an abstract
class by defining a "pure virtual function" in the class, which is like
Flavors's :REQUIRED-METHOD declaration; this indicates that classes in that
hierarchy are required to implement that method, because some methods in
the superclass will do the equivalent of (<method> self ...) or
this-><method>(...). I think Java's "interface" classes are similar.

Most OO languages don't provide any way to define protocols in any rigorous
fashion. I think Xerox PARC's research into Aspect-Oriented Programming
tries to include this, but as far as established OO languages go, abstract
classes and interface classes are about the only thing there is.

--
Barry Margolin, bar...@genuity.net
Genuity, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.

Raymond Toy

unread,
May 24, 2000, 3:00:00 AM5/24/00
to
>>>>> "Tim" == Tim Bradshaw <t...@cley.com> writes:

Tim> Yes, but you can't write this method-of-the-subclass, because you
Tim> don't know what it should do. The default primary method for
Tim> INITIALIZE-INSTANCE is system-supplied, and although there's
Tim> information about what it does (call SHARED-INITIALIZE), it's not
Tim> clear to me that that is *all* it does. The obvious approach to
Tim> overriding the default primary method would be something that at some
Tim> point does (CALL-NEXT-METHOD) to make sure the default behaviour
Tim> happens (or just define an :AFTER method in the normal way...), but
Tim> this won't work in this case because you have to hop over (several
Tim> levels of) superclass to actually get at the method you want to call.

Tim> Actually, I think I'm wrong about all this -- hyperspec Section 7.1.7
Tim> seems to mean that it is the case that all INITIALIZE-INSTANCE does is
Tim> call SHARED-INITIALIZE, so you could define your own methods for it.
Tim> (I guess I should elide all the previous para now...).

Yes, if I delete that paragraph, I feel much better about my limited
understanding of CLOS---I wasn't totally off base. :-)

Tim> It would still be a bit clunky I think as you'd either have to do some
Tim> cleverness with mixing classes (define a method signalling an error on
Tim> ABSTRACT-CLASS, and one that does the default thing on
Tim> NON-ABSTRACT-CLASS, then you can define non-abstract subclasses by
Tim> ensuring NON-ABSTRACT-CLASS ends up ahead of ABSTRACT-CLASS in the
Tim> CPL:

Tim> (defclass noninstantiable (... abstract-class ...) ...)
Tim> (defclass instantiable (... non-abstract-class
Tim> ... noninstantiable ...) ...)

Tim> ) or define lots of methods for INITIALIZE-INSTANCE for all the
Tim> subclasses.

Yes, this sounds quite messy if you really want to use abstract-class
with multiple-inheritance. Fortunately, I haven't had any reason to
go down that path with my CLOS stuff.

Ray

Craig Brozefsky

unread,
May 25, 2000, 3:00:00 AM5/25/00
to
Barry Margolin <bar...@genuity.net> writes:

> Basically, in languages that have them, abstract classes are a way
> of encoding some rules about the protocol that the class hierarchy
> is intended to implement within the class definitions. In C++, you
> create an abstract class by defining a "pure virtual function" in
> the class, which is like Flavors's :REQUIRED-METHOD declaration;
> this indicates that classes in that hierarchy are required to
> implement that method, because some methods in the superclass will
> do the equivalent of (<method> self ...) or this-><method>(...). I
> think Java's "interface" classes are similar.

How does this work in Flavors? I am very unfamiliar with Flavors.
Are method definitions lexically connected with class definitions? If
not, how would the system know when to say, "Hey buddy, you forgot
this required method!"

In CLOS this doesn't make much sense unless you wanna add a method
which uses the MOP to scan a set of generic functions to make sure
they have a method specialized on a given class. But when do you
wanna run that code? At the end of your system load or something? I
suppose that would be of some use.

--
Craig Brozefsky <cr...@red-bean.com>
Lisp Web Dev List http://www.red-bean.com/lispweb
--- The only good lisper is a coding lisper. ---

Barry Margolin

unread,
May 25, 2000, 3:00:00 AM5/25/00
to
In article <87d7mai...@piracy.red-bean.com>,

Craig Brozefsky <cr...@red-bean.com> wrote:
>Barry Margolin <bar...@genuity.net> writes:
>
>> Basically, in languages that have them, abstract classes are a way
>> of encoding some rules about the protocol that the class hierarchy
>> is intended to implement within the class definitions. In C++, you
>> create an abstract class by defining a "pure virtual function" in
>> the class, which is like Flavors's :REQUIRED-METHOD declaration;
>> this indicates that classes in that hierarchy are required to
>> implement that method, because some methods in the superclass will
>> do the equivalent of (<method> self ...) or this-><method>(...). I
>> think Java's "interface" classes are similar.
>
>How does this work in Flavors? I am very unfamiliar with Flavors.
>Are method definitions lexically connected with class definitions? If
>not, how would the system know when to say, "Hey buddy, you forgot
>this required method!"

Although Flavors associates methods with specific flavors, like most other
OO languages, it's like CLOS in that methods are not lexically part of the
flavor definition. The Chinual says "This keyword allows the error of
having no handler for the message to be detected when the flavor is defined
(which usually means at compile time) rather than at run time."

>In CLOS this doesn't make much sense unless you wanna add a method
>which uses the MOP to scan a set of generic functions to make sure
>they have a method specialized on a given class. But when do you
>wanna run that code? At the end of your system load or something? I
>suppose that would be of some use.

Flavors has a macro (compile-flavor-methods &rest flavor-names) that can be
put in a file to be compiled. When this form is compiled, all the combined
methods are genated and put into the resulting object file. I think this
will perform the :REQUIRED-METHOD consistency checking. If you don't use
this, then the consistency checking will indeed be deferred to run-time,
the first time an instance of a sub-flavor is instantiated.

I think most CLOS implementations have something that's analogous to this,
to precompile its data structures.

Hartmann Schaffer

unread,
May 26, 2000, 3:00:00 AM5/26/00
to
In article <87d7mai...@piracy.red-bean.com>,
Craig Brozefsky <cr...@red-bean.com> writes:
> ...

> In CLOS this doesn't make much sense unless you wanna add a method
> which uses the MOP to scan a set of generic functions to make sure
> they have a method specialized on a given class. But when do you

CLOS'es object model doesn't exactly tie methods to one class.

> wanna run that code? At the end of your system load or something? I
> suppose that would be of some use.
>

--

Hartmann Schaffer


0 new messages