Erik Naggum <e...@naggum.no> writes: > * "Sergio" <sergi...@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.
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.)
> > * "Sergio" <sergi...@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.
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 :(((
In article <m33dn89tfo....@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.
* Friedrich Dominicus <Friedrich.Domini...@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.
#:Erik -- If this is not what you expected, please alter your expectations.
"Sergio" <sergi...@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)
> 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.
-- (Rmz)
Bj\o rn Remseth !Institutt for Informatikk !Net: r...@ifi.uio.no Phone:+47 91341332!Universitetet i Oslo, Norway !ICBM: N595625E104337
In article <lTwW4.1683$Qk4.16...@telenews.teleline.es>, "Sergio" <sergi...@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 :(((
> 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.
(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))))
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!
> 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"))
* 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.
#:Erik -- If this is not what you expected, please alter your expectations.
> 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 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 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.
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:
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.
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.
> * 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.
Friedrich Dominicus <Friedrich.Domini...@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!
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)
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.
> 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.