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

making virtual functions in CL

41 views
Skip to first unread message

David Bakhash

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
Hi,

I'm trying to figure out how to make a virtual function. i.e. suppose I
have a CLOS class called `entry':

(defclass entry () ())

and I just want to insure that each entry has a method called `id' so
that for all subclasses of entry I can do:

(id my-entry)

and get back something. I don't want to add a slot called `id' in entry
b/c that might be less efficient. Actually, I'm not even sure that
this is possible in CL (and if it isn't, then that's a bit
dissappointing). But is there a way? Is it possible to use
`defgeneric' in order to do this?

thanks,
dave

vnik...@poboxes.com

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
In article <wkhftv2...@mit.edu>,

David Bakhash <ca...@mit.edu> wrote:
> Hi,
>
> I'm trying to figure out how to make a virtual function. i.e. suppose I

Virtual? Where does this word come from? (Any similarities to
a language whose name starts with a `C' are purely coincidental?)

> have a CLOS class called `entry':
>
> (defclass entry () ())
>
> and I just want to insure that each entry has a method called `id' so
> that for all subclasses of entry I can do:
>
> (id my-entry)
>
> and get back something. I don't want to add a slot called `id' in entry
> b/c that might be less efficient. Actually, I'm not even sure that
> this is possible in CL (and if it isn't, then that's a bit
> dissappointing). But is there a way?

(defmethod id ((x entry)) 'something)

> Is it possible to use
> `defgeneric' in order to do this?

Yes, sure:

(defgeneric id (x)
(:method ((x entry)) 'something))

>
> thanks,
> dave

No sweat,
Vassil.


Vassil Nikolov <vnik...@poboxes.com> www.poboxes.com/vnikolov
(You may want to cc your posting to me if I _have_ to see it.)
LEGEMANVALEMFVTVTVM (Ancient Roman programmers' adage.)

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/ Search, Read, Discuss, or Start Your Own

joa...@kraut.bc.ca

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
David Bakhash <ca...@mit.edu> writes:
>
> I'm trying to figure out how to make a virtual function.
> i.e. suppose I have a CLOS class called `entry':

>
> (defclass entry () ())
>
> and I just want to insure that each entry has a method called `id'
> so that for all subclasses of entry I can do:
>
> (id my-entry)

(defmethod id ((self entry))
'entry-id)

(defmethod id ((self my-entry))
'my-entry-id)

Joachim

--
joa...@kraut.bc.ca (http://www.kraut.bc.ca)
joa...@mercury.bc.ca (http://www.mercury.bc.ca)

Seth Tisue

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to

I'm a little unclear on what it is you're asking for, since "virtual
function" in C++ means a cluster of distinct things, which don't
necessarily go together in other languages.

In CLOS, all methods are virtual in the sense that they can be
overridden in subclasses, are dispatched at runtime, etc. If you want
to set what the lambda list is, but not supply any implementation, use
DEFGENERIC.

If you want to enforce that no one may create a subclass without also
implementing the method for that subclass, then you're out of
luck... CLOS doesn't do that. After all, how could it? Method
declarations are not lexically contained within class definitions, as
they are in C++.
--
== Seth Tisue <s-t...@nwu.edu> http://www.cs.nwu.edu/~tisue/
"Thanks to movies, I can vividly picture how I might be violently
removed from existence." - Eric Goldstein

David Bakhash

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
I don't like this at all. I specifically don't want to a defmethod for
the base class. I guess if I did, then it would just return an error,
though. Isn't there a better way using defgeneric?

dave

David Bakhash

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to
ti...@cs.nwu.edu (Seth Tisue) writes:

> If you want to enforce that no one may create a subclass without also
> implementing the method for that subclass, then you're out of
> luck... CLOS doesn't do that. After all, how could it? Method
> declarations are not lexically contained within class definitions, as
> they are in C++.

Maybe you can't make the compiler complain about it, but I think you can
make the program complain properly at runtime:

(defgeneric id (entry)
(:method (entry)
(error "subclasses of class `entry' must define an `id' method.")))

this, I think, is what I was talking about.

dave

Lyman S. Taylor

unread,
Jan 13, 1999, 3:00:00 AM1/13/99
to

If I recall the C++ terminology correctly , there is no concpet
of a "pure virtual" in CLOS. A "method" that all subclasses
must provide, but there is no default "behaviour".

Methods don't "belong" to classes. Methods "belong" to a generic
function. Or in other words, the name "id" is not unique to ( or
"owned by" or "member of") any class. You will have to "tell" the
generic function that other classes are inappropriate as agruments.

(defgeneric id (x)
(:method (( x t)
(error "The ~A object: ~A is not an appropriate arg to id"
(type-of x) x ))))

Then add methods for the appropriate clases by invoking DEFMETHOD.

There is no way to stop other additions. Other class hierarchies can
also make use of the this generic function. The only "interface"
methods must adhere to is that of the parameter list of the generic
function.

If your intent is that only subclasses of entry be added then

(defgeneric id ( (x entry ) )
... )

would be the explicit way to make that intent known to those who might
later extent the generic function. The language won't "enforce" this
though.

--

Lyman S. Taylor "Twinkie Cream; food of the Gods"
(ly...@cc.gatech.edu) Jarod, "The Pretender"


Erik Naggum

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
* David Bakhash <ca...@mit.edu>

| I'm trying to figure out how to make a virtual function.

that's pretty easy.

class foo { public: virtual bar() = 0; }

(please note that this is the _correct_ answer to your question.)

| i.e. suppose I have a CLOS class called `entry':
|
| (defclass entry () ())
|
| and I just want to insure that each entry has a method called `id' so
| that for all subclasses of entry I can do:
|
| (id my-entry)
|

| and get back something.

yeah, you would use a virtual function for this kind of trivial problem
in C++, but C++ is nigh the only language in which you would use a
virtual function for this kind of trivial problem.

| I don't want to add a slot called `id' in entry b/c that might be less
| efficient.

yes, that's good programming methodology. first, we worry about what
_might_ be less efficient, because we consider only the options that are
likely to be efficient without knowing what we want to accomplish. that
is always the best course of action, since machine cycles cost a hell of
a lot more than programmer time these days. the rule of thumb is:
optimal use of machines cycles first, semantics later, if we have some
extra time after having ensured that no machine cycle is wasted.

| Actually, I'm not even sure that this is possible in CL (and if it isn't,
| then that's a bit dissappointing).

of course it isn't possible in Common Lisp! virtual functions in C++ is
there because the language is broken at the very core of its design.
virtual function is the fix to that problem. Common Lisp doesn't have
the design flaw, so it's pretty damn hard to conjure up a fix for it
without first breaking Common Lisp the same way C++ is broken.

| But is there a way? Is it possible to use `defgeneric' in order to do
| this?

your first task should be to stop thinking in C++ if you want to solve
any actual problems. C++ programmers have to invent these things because
they think in the wrong terms, especially the efficiency fallacy.

second, ask yourself what you _really_ want to accomplish. if all you
want to know about an instance is of which class it is an instance, well,
duh, CLASS-OF does that. (and TYPE-OF returns the name of the class as a
symbol, if that's more convenient.) other information can be allocated
at class slots, but you might do that (manually) in every class, which
naturally lends itself to the more advanced solution: meta-classes that
remember properties of the classes. if meta-classes are too advanced,
there's a simpler solution: use the class as the key in an alist or hash
table and make functions operate on the information thus retrieved --
this implements meta-class semantics by better-known techniques, the same
way one would have done object-oriented programming before the languages
supported it.

to me, you request looks like you're trying to reinvent C++ in CLOS, and
you end up fighting against _both_ languages.

#:Erik

Kent M Pitman

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
David Bakhash <ca...@mit.edu> writes:

> I'm trying to figure out how to make a virtual function. i.e. suppose I


> have a CLOS class called `entry':
>
> (defclass entry () ())
>
> and I just want to insure that each entry has a method called `id' so
> that for all subclasses of entry I can do:
>
> (id my-entry)
>

> and get back something. I don't want to add a slot called `id' in entry
> b/c that might be less efficient. Actually, I'm not even sure that


> this is possible in CL (and if it isn't, then that's a bit

> dissappointing). But is there a way? Is it possible to use


> `defgeneric' in order to do this?

It's not clear to me that this is as meaningful as you think it is
since it either implies that it's good enough to have only one such
definition above you in the type tree or else it implies that you have
to write redundant methods where you don't need them. That is, if you
have a class tree like:

A
/ \
B C
/ \
D E

and you want to put a method like this on A, then you are either saying
that B,C,D,E must implement it (which is an impediment to the definition
of D and E using object-oriented programming if C impementing it is
"enough") or else you are saying that B,C is sufficient (which risks
that the subclassing of C into D and E is drastic enough that you
will end up not catching it because you were only looking for one
definition above any given point and you weren't fussy enough about which).
All in all, you end up having to trust somewhere along the way unless
you have total control of the code. In one case, you are trusting the
guy who defines the function that his CLAIM that all classes would need
this definition is really true. In the other case, you are trusting
that the guys beneath you will really implement what you want them to.

You can make programmatic enforcement of either interpretation, but
nothing substitutes for perfect information, and you should not confuse
making a programmatic requirement with assuring the absence of program
bugs.

I'm sure there's something in the MOP that will allow you to do what
you want by making a metaclass that allows required methods. It's not
that unreasonable if you understand that it is isn't the answer to all
the world's problems. But don't expect too much of it. Incidentally,
Flavors (one ancestor of CLOS) had a :REQUIRED-METHODS (I think that
was the name) in DEFFLAVOR (the DEFCLASS equivalent), and I think it
did pretty much exactly what you wanted. It didn't make it into CL
for some reason--not sure why. But IMO the name "virtual" is just
C++ baggage that is only confusing in this query. Dylan uses the
term "virtual" correctly. A virtual slot is about having a slot that
is computed rather than reprsented explicitly (such as an AREA slot
for a rectangle that already has LENGTH and WIDTH). Virtual slots
probably want to be required methods, but required methods are not all
virtual slots. And what you described was a required method, not
a virtual slot.

Incidentally, DEFGENERIC is not the right place for this info.
That would be a modularity violation, IMO. It has no business
knowing about who will implement things. Not if it's REALLY
a generic function. it's about defining a protocol. DEFCLASS
is about saying how protocols defined elsewhere map into a
certain situation. More than one class might want to have required
methods for a given generic function. It would be goofy for a
generic function to have a list of those classes as if somehow
it knew the nature of that patchwork quilt...

vnik...@poboxes.com

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to
In article <77itpi$7qp$1...@Godzilla.cs.nwu.edu>,
ti...@cs.nwu.edu (Seth Tisue) wrote:
(...)

> If you want to enforce that no one may create a subclass without also
> implementing the method for that subclass, then you're out of
> luck... CLOS doesn't do that.

One could have the method for the class signal an error
which would (sort of) force the programmer to implement
the method for the subclass to do something more useful.
This could be appropriate at least in some cases.

Thomas A. Russ

unread,
Jan 14, 1999, 3:00:00 AM1/14/99
to

At the risk of doing battle with the redoubtable Erik Naggum, I will
weigh in here.

The original request from David Bakhash seems to be reasonable. He
wants to define a class and impose a restriction that all subclasses
must implement a given method in order to fulfill the interface contract
for the class -- but where there is no general method for the
superclass. (The proper term in C++ is PURE virtual function)

This is a useful semantic concept when one is defining abstract
superclasses, ie, classes which are not expected to be instantiated
directly. This is typically done in order to have a common interface,
or in situations where some portion of the functionality can be
implemented by methods on the abstract class, but for which some
additional concrete methods are required for proper functioning.

This does not seem like an unreasonable request.

In CLOS, the best that you can do is define a method on the class which
signals an error, but this forces you to discover the problem at run
time rather than at compile time. A notion of an abstract class with
required methods might be a nice extension to the Common Lisp standard.

As an aside, this is something which has been present in some Lisp
systems (although not in Common Lisp). Symbolic's Flavors had a class
option called required-methods which listed methods that all subclasses
had to implement in order to be conforming.

--
Thomas A. Russ, USC/Information Sciences Institute t...@isi.edu

Marco Antoniotti

unread,
Jan 15, 1999, 3:00:00 AM1/15/99
to

Kent M Pitman <pit...@world.std.com> writes:

...


>
> I'm sure there's something in the MOP that will allow you to do what
> you want by making a metaclass that allows required methods. It's not
> that unreasonable if you understand that it is isn't the answer to all
> the world's problems. But don't expect too much of it. Incidentally,
> Flavors (one ancestor of CLOS) had a :REQUIRED-METHODS (I think that
> was the name) in DEFFLAVOR (the DEFCLASS equivalent), and I think it
> did pretty much exactly what you wanted. It didn't make it into CL
> for some reason--not sure why.

Maybe a simple reason is the fact the FLAVORS where essentially
'singly dispatching', while it is more complex to define such a thing
for a multi method?

> But IMO the name "virtual" is just
> C++ baggage that is only confusing in this query. Dylan uses the
> term "virtual" correctly. A virtual slot is about having a slot that
> is computed rather than reprsented explicitly (such as an AREA slot
> for a rectangle that already has LENGTH and WIDTH). Virtual slots
> probably want to be required methods, but required methods are not all
> virtual slots. And what you described was a required method, not
> a virtual slot.
>
> Incidentally, DEFGENERIC is not the right place for this info.
> That would be a modularity violation, IMO. It has no business
> knowing about who will implement things. Not if it's REALLY
> a generic function. it's about defining a protocol. DEFCLASS
> is about saying how protocols defined elsewhere map into a
> certain situation. More than one class might want to have required
> methods for a given generic function. It would be goofy for a
> generic function to have a list of those classes as if somehow
> it knew the nature of that patchwork quilt...

I have been thinking along these lines for some time. I.e. somehow
it'd be nice to have a PROTOCOL something (the term 'object' is too
overloaded :) ) which could tell some code checker (maybe the
compiler?!? :) ) that a certain set of methods ("belonging to the
protocol" - quotes mandatory for the time being) is "present" in the
system.

I have no idea how to provide such a thing. :)

Cheers

--
Marco Antoniotti ===========================================
PARADES, Via San Pantaleo 66, I-00186 Rome, ITALY
tel. +39 - (0)6 - 68 10 03 17, fax. +39 - (0)6 - 68 80 79 26
http://www.parades.rm.cnr.it

Marco Antoniotti

unread,
Jan 15, 1999, 3:00:00 AM1/15/99
to

NOISE

David (Bakhash), what is your true email address?

Barry Margolin

unread,
Jan 15, 1999, 3:00:00 AM1/15/99
to
In article <lwbtk0b...@copernico.parades.rm.cnr.it>,

Marco Antoniotti <mar...@copernico.parades.rm.cnr.it> wrote:
>
>Kent M Pitman <pit...@world.std.com> writes:
>
> ...
>>
>> I'm sure there's something in the MOP that will allow you to do what
>> you want by making a metaclass that allows required methods. It's not
>> that unreasonable if you understand that it is isn't the answer to all
>> the world's problems. But don't expect too much of it. Incidentally,
>> Flavors (one ancestor of CLOS) had a :REQUIRED-METHODS (I think that
>> was the name) in DEFFLAVOR (the DEFCLASS equivalent), and I think it
>> did pretty much exactly what you wanted. It didn't make it into CL
>> for some reason--not sure why.
>
>Maybe a simple reason is the fact the FLAVORS where essentially
>'singly dispatching', while it is more complex to define such a thing
>for a multi method?

It would complicate it a bit -- in addition to specifying the name of the
required method, you would also have to indicate which parameter was
supposed to be specialized on a subclass of the class with the option.

However, IIRC, :REQUIRED-METHODS wouldn't really do what the original
poster wanted. I think he wanted a way to require that every leaf class
define its own version of the method (e.g. a method that returns the name
of the class). :REQUIRED-METHODS, like C++'s pure virtual functions,
simply require that some method applicable to the object be defined in
order for a class to be instantiated. It might be defined in some
intermediate class, not necessarily the leaf class. In C++, each
intermediate class can ensure that his descendents must redefine it by
declaring it private, but if any of them declares it public then it gets
inherited down the chain; there's nothing abstract base class definer can
do to prevent this.

>> But IMO the name "virtual" is just
>> C++ baggage that is only confusing in this query. Dylan uses the
>> term "virtual" correctly. A virtual slot is about having a slot that
>> is computed rather than reprsented explicitly (such as an AREA slot
>> for a rectangle that already has LENGTH and WIDTH). Virtual slots
>> probably want to be required methods, but required methods are not all
>> virtual slots. And what you described was a required method, not
>> a virtual slot.

What the original poster was describing is called, in C++, a "pure virtual
function". As has been noted elsewhere, all methods in CLOS are virtual
(they dispatch on the dynamic type); it's the "purity" that the original
poster was really after -- this is the quality that requires a subclass to
redefine the method.

>I have been thinking along these lines for some time. I.e. somehow
>it'd be nice to have a PROTOCOL something (the term 'object' is too
>overloaded :) ) which could tell some code checker (maybe the
>compiler?!? :) ) that a certain set of methods ("belonging to the
>protocol" - quotes mandatory for the time being) is "present" in the
>system.

I think Xerox PARC's Aspect-Oriented Programming research includes things
like this.

--
Barry Margolin, bar...@bbnplanet.com
GTE Internetworking, Powered by BBN, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Don't bother cc'ing followups to me.

Erik Naggum

unread,
Jan 17, 1999, 3:00:00 AM1/17/99
to
* t...@sevak.isi.edu (Thomas A. Russ)

| This is a useful semantic concept when one is defining abstract
| superclasses, ie, classes which are not expected to be instantiated
| directly. This is typically done in order to have a common interface, or
| in situations where some portion of the functionality can be implemented
| by methods on the abstract class, but for which some additional concrete
| methods are required for proper functioning.
|
| This does not seem like an unreasonable request.

no, it isn't: it caused the concept of "interfaces" in Java. however,
the "virtual" _implementation_ of this request as found in C++ is
inherently flawed and should not be replicated anywhere.

there are many ways to enforce a protocol or interface. a language may
implement one way to do it, and that's all you get. Common Lisp is not
similarly constrained. if you want to specify an interface that should
cause all classes to implement their own methods, we should not look at
how C++ was broken and put back toghether with duct tape -- the duct tape
is not hold the solution.

suppose a generic function would refuse to find methods for classes with
certain properties or relationships (David has already implicated the
generic functions, btw), such that there would be no danger of calling
the method that "belongs" to a superclass, would it operationally define
an interface? we have method classes, generic function classes, and
method combination types at our command. can something be built with
them that ignors the class precedence list and so does not find methods
for superclasses of some or any of its arguments? this does violate the
spirit of inheritance, but I believe the whole purpose here is to _have_
"disinherited" interface functions.

let's do something C++ couldn't do: let's get it right.

#:Erik
--
SIGTHTBABW: a signal sent from Unix to its programmers at random
intervals to make them remember that There Has To Be A Better Way.

Kent M Pitman

unread,
Jan 17, 1999, 3:00:00 AM1/17/99
to
t...@sevak.isi.edu (Thomas A. Russ) writes:

> At the risk of doing battle with the redoubtable Erik Naggum, I will
> weigh in here.

Hi there. Glad to see you chime in. Mind if an alternate battler
steps in, just to keep things lively? :-)

> The original request from David Bakhash seems to be reasonable. He
> wants to define a class and impose a restriction that all subclasses
> must implement a given method

I agree this is a reasonable goal.

> in order to fulfill the interface contract
> for the class -- but where there is no general method for the
> superclass.

I think this focuses on where I have a quibble. As a matter of
personal belief, if nothing else, I think it's fine to want all
subclasses to implement a given method, but its quite a leap to say
that because they have implemented it, they have satisfied an inteface
contract. It's sort of like saying that writing a yellow sticky note
saying "remember to fill out tax forms" is going to assure I've done
my taxes correctly. A reminder is a good thing for forgetful people,
but it is a far sight short of a proof that a correct impementation
has occurred. It might keep me from forgetting to send my tax forms,
but it won't detect the difference between needing to fill out a
Schedule C or not. I might have a method for filling out taxes that
is wrong, and no amount of reminders to fill out my taxes will
substitute for a knowledge of how to correctly fill them out.
So I am not arguing against wanting the reminders, but I'm saying
it's just a device and not something that really adds fundamental
power to the language nor is it something that if absent leaves the
language fundamentally crippled.

It's more analogous to things like WHEN and UNLESS that make the
language more useful and less error-prone but that don't semantically
extend IF in a real way. I complain when WHEN and UNLESS are absent
in other languages and dialects because I think it's a difference that
matters, but I don't think the -reason- it matters is one of assuring
correctness, except in the very limited sense that anything that
statistically makes idiosyncratic, error-prone humans a little more
comfortable has a corresponding statistical improvement in program
maintainability. Quite a noble goal, and for this I support the idea
of catering to such odd preferences people have. But not because
it "fulfills an interface contract".

Well, of course, one can write a contract about anything at all. One
-can-, and often does, write a contract with one employees that asks
them to appear at a certain time for work. (For some, like cashiers
in a store who have to serve people who arrive at the same time, this
make some sense. But assume I mean some like programmers whose
project isn't due on the day in question.) The requirement is
intended to assure that the employee is working, but the requirement
really doesn't measure that the employee is working; more, it
redefines what it is to -be- working. The measure of whether someone
is present in physical space becomes the measure of whether a company
is moving along, and then there is great surprise when later something
isn't done. It would have been better to measure progress on goals,
but that's hard to do--so we substitute something simpler. And,
threading up the analogy now, it would be better to measure whether
a program was really "getting filled in". But we don't always know
when that is. So we invent ways to slap someone's wrist when they
make a superficial violation of protocol. And we hope that's enough
to put the fear of God (or fear of Compiler Warnings, as the case
may be) in them, and that the rest will "just happen".

> (The proper term in C++ is PURE virtual function)

This isn't my area of expertise, but sounds more right.

> This is a useful semantic concept when one is defining abstract
> superclasses, ie, classes which are not expected to be instantiated
> directly. This is typically done in order to have a common interface,
> or in situations where some portion of the functionality can be
> implemented by methods on the abstract class, but for which some
> additional concrete methods are required for proper functioning.

Yes, although it does not detect that perhaps additional concrete
methods are needed when additional subclassing has occurred. Neither
does it detect whether the user put the right text in the required
methods.



> This does not seem like an unreasonable request.

Answered above.

> In CLOS, the best that you can do is define a method on the class which
> signals an error, but this forces you to discover the problem at run
> time rather than at compile time. A notion of an abstract class with
> required methods might be a nice extension to the Common Lisp standard.

To the extent that it's useful to notice this at all, I agree with you
that Common Lisp is deficient for not having a way to notice it early.
I guess what I wish,though, is that CL had a more general mechanism
for noticing that the class was "as complete as it was going to get
and about to be compiled" and for "allowing arbitrary programs to be
run to do arbitrary tests, perhaps not all just syntactic". The MOP
offers some of that, but perhaps not as much as should be needed... CL
needs a more elaborated sense of things like "class sealing" (in the
Dylan sense) that announce that parts of the construction of a
distributed/modular system are all in place and that it's time for an
aggressive checking and optimziation of whatever needs to be checked
and optimized. Absent such a language-level declaration, the MOP has
no time to trigger because it has to worry that more changes will happen
later and that an error will be premature.

> As an aside, this is something which has been present in some Lisp
> systems (although not in Common Lisp). Symbolic's Flavors had a class
> option called required-methods which listed methods that all subclasses
> had to implement in order to be conforming.

Yes. And I think there was also a (compile-flavor-methods foo) that
you put in your system stuff at the end of the build to say "you've
seen the whole system now--this would be a good time to check that
kind of stuff. Because absent that, I think you had to wait until
MAKE-INSTANCE time to find out that required methods were not present,
since otherwise you had no way of telling that the method wasn't still
in another file yet to be loaded. For what it's worth, my notes say
that Gregor Kiczales (MOP author) had agreed by Mar 91 that something
like this was needed, but that we had no spcific proposal and it fell
through the cracks. (My notes after the absence of a specific
proposal say "Still no proposal. Sigh... users are not gonna like
this.") So later, in my Public Review comments on the draft
specification, I proposed adding something equvialent to
compile-flavor-methods for CL that either took a method name or a
class name or a method signature and forced compilation/checking of
the appropriate methods. But the committee opted not to act on this
either. Ah well. I didn't really expect them to. I just wanted to
be on record as having known this was an issue of importance so that
when fingers started getting pointed, I wasn't among those who was
pointed at. So don't take any of my above remarks as saying we
shouldn't have the feature being discussed--I'm just talking about
the philosophical underpinnings of the want.


Harley Davis

unread,
Jan 17, 1999, 3:00:00 AM1/17/99
to
Erik sez...

>* t...@sevak.isi.edu (Thomas A. Russ)
>| This is a useful semantic concept when one is defining abstract
>| superclasses, ie, classes which are not expected to be instantiated
>| directly. This is typically done in order to have a common interface, or
>| in situations where some portion of the functionality can be implemented
>| by methods on the abstract class, but for which some additional concrete
>| methods are required for proper functioning.
>|
>| This does not seem like an unreasonable request.
>
> no, it isn't: it caused the concept of "interfaces" in Java. however,
> the "virtual" _implementation_ of this request as found in C++ is
> inherently flawed and should not be replicated anywhere.


Actually, Java's interfaces are, IMO, quite flawed because they do not allow
you to define partial implementations of protocols as Russ mentions above.
They only allow you to define the set of method names and argument types for
a particular protocol.

C++'s abstract base classes are much more flexible as they do allow you to
define an appropriate mix of pure interface specifications (pure virtual
functions) and implementations.

Common Lisp is pretty good for this but has no way to specify just
interfaces with no implementation whatsoever - you need to explicitly raise
a runtime error in methods which you want to specify in a superclass but
implement only in a subclass. Similarly, there is no way to statically
specify abstract classes and it is kind of a pain to even signal the right
error for instantiating supposedly abstract classes. This may delay the
programmer in finding incomplete concrete class definitions but follows the
generally dynamic and late-binding nature of the language, and has about the
same ramifications as runtime type checking.

I am not clear as to why Erik feels that C++ is especially flawed for this,
other than that its syntax is screwy as usual (no keywords for abstract
classes; need to define a pure virtual to make a base class abstract; pure
virtual function definition syntax is silly.) There is a reasonable
argument that C++ simply lets you specify too much or requires you to
overdesign your class hierarchies to take advantage of all the static
declarations you can make, but I'm not sure if this is what Erik is getting
at.

BTW, I have played around in Lisp with various ways to define abstract
protocols declaratively rather than dynamically. I think it is fairly easy
to find a solution using the MOP and some additional macros/keywords as Erik
suggests. But it's not very easy getting the compiler to check the result
as in C++ and Java. (FYI, in C++ and Java the compiler will not normally
let you instantiate an abstract class.)

-- Harley

Erik Naggum

unread,
Jan 18, 1999, 3:00:00 AM1/18/99
to
* "Harley Davis" <spamless_davis@spamless_ilog.com>

| I am not clear as to why Erik feels that C++ is especially flawed for
| this, other than that its syntax is screwy as usual (no keywords for
| abstract classes; need to define a pure virtual to make a base class
| abstract; pure virtual function definition syntax is silly.) There is a
| reasonable argument that C++ simply lets you specify too much or requires
| you to overdesign your class hierarchies to take advantage of all the
| static declarations you can make, but I'm not sure if this is what Erik
| is getting at.

the problem with C++ is that it pretends to give you something, but fails
to follow up on it and actually deliver it. (this is not unlike most C++
projects, but I digress.) in this case, a pure virtual function is a
good idea that never got anywhere. it tries to answer the desire for a
protocol between class designer and subclass implementor, but only the
first subclass needs to implement it, yet if that one forgets it, some
subclass of that class might need to, and despite all the "statically
typed" nonsense, it doesn't really enforce any of this protocol. it's
worse to pretend you give somebody something valuable than not to give it.

| BTW, I have played around in Lisp with various ways to define abstract
| protocols declaratively rather than dynamically. I think it is fairly
| easy to find a solution using the MOP and some additional macros/keywords
| as Erik suggests. But it's not very easy getting the compiler to check
| the result as in C++ and Java. (FYI, in C++ and Java the compiler will
| not normally let you instantiate an abstract class.)

sure, but that's the only bonus you get, and it isn't the one you want,
which is a way to make sure subclasses that change something that the
virtual function needs will also supply an implementation of that
function. this is actually fairly tricky stuff. C++ has taken the easy
way out and did something that looks good to people who don't care to
think too much about what they get. stuff like that really bugs me.

Barry Margolin

unread,
Jan 18, 1999, 3:00:00 AM1/18/99
to
In article <31256652...@naggum.no>, Erik Naggum <er...@naggum.no> wrote:
> the problem with C++ is that it pretends to give you something, but fails
> to follow up on it and actually deliver it. (this is not unlike most C++
> projects, but I digress.) in this case, a pure virtual function is a
> good idea that never got anywhere. it tries to answer the desire for a
> protocol between class designer and subclass implementor, but only the
> first subclass needs to implement it, yet if that one forgets it, some
> subclass of that class might need to, and despite all the "statically
> typed" nonsense, it doesn't really enforce any of this protocol. it's
> worse to pretend you give somebody something valuable than not to give it.

This is similar to Kent's point that it's not good enough just to provide
an implementation of a name -- that doesn't guarantee that it actually
implements the protocol.

Unfortunately, there's only so much that you can expect from static
checking like this. It's not much different from what type checking gets
you -- they can tell you whether the program is "well formed" in some
respects, but not whether it's "correct". E.g., declaring a function named
"add1" to take and return an integer doesn't prevent it from subtracting
rather than adding.

Some languages allow you to attach preconditions and postconditions to
statements and functions, to try to capture more of the requirements of a
protocol. Some of these things may be statically checkable. AFAIK, this
type of stuff is still mostly in research languages, it hasn't reached the
mainstream much. And it's not clear to me how useful they really are;
considering my above "add1" example, how many programmers would really
write the postcondition after every call to it that the result is 1 more
than the original value? But I suppose they can be useful on a coarse
scale (I've never actually used such a language, so I don't really know
what it's like).

Pierre Mai

unread,
Jan 19, 1999, 3:00:00 AM1/19/99
to
Barry Margolin <bar...@bbnplanet.com> writes:

> Some languages allow you to attach preconditions and postconditions to
> statements and functions, to try to capture more of the requirements of a
> protocol. Some of these things may be statically checkable. AFAIK, this
> type of stuff is still mostly in research languages, it hasn't reached the
> mainstream much. And it's not clear to me how useful they really are;

Well, the simple pre- and post-condition type of language has already
"hit the mainstream", as in Bertrand Meyer's Eiffel. Matthias Hoelzl
has even developed a prototypical implementation of
"Design-By-Contract" for CLOS[1].

> considering my above "add1" example, how many programmers would really
> write the postcondition after every call to it that the result is 1 more
> than the original value? But I suppose they can be useful on a coarse
> scale (I've never actually used such a language, so I don't really know
> what it's like).

Well, the post-condition would of course be attached to the add1
function, such that the pre- and post-conditions are checked
automatically on every invocation of add1 (otherwise this kind of
thing would be less useful than it actually is). Indeed Eiffel and
the implementation of DBC in CLOS offer some convenience features,
like class-invariants, which are checked as pre- and post-conditions
on all method invocations for any object of a given class.

But on the whole DBC doesn't address the protocol-definition problem
we where talking about, since it has no real meta-level, that allows
you to talk about the methods/functions belonging to a protocol, their
interrelation, etc.

I think that this kind of protocol definition could be added to Common
Lisp with less problems than to languages in the "static-only" camp,
since in CL one isn't restricted to static checking, and one has much
better access to the meta-level that is needed in formulating many of
the more relevant conditions.

Given a facility that allows one to define fairly expressive
conditions on the structure and/or behaviour of a system, one could
define a function like the COMPILE-FLAVOR-METHODS Kent mentioned
(probably better named), which could be invoked after the system was
loaded (and/or periodically thereafter, e.g. when new sub-systems are
loaded, the running system is modified, or just to check that
everything is still in order) to check most of the "statically"
resolvable conditions. Those conditions which can only be checked at
"run-time", would be checked at run-time, like in Hölzl's DBC.

I think that this kind of functionality is easily implemented in CL,
but is not that simple to design right, since the kind of properties
that should be checkable would probably vary from system to system and
programmer to programmer... OTOH I think that MOPs/Aspect-Oriented
Programming should cover some of this territory quite well, and I'd be
surprised, if there weren't clever research in this area to be
consulted...[2]

Regs, Pierre.

Footnotes:
[1] Which goes to show that with CL it isn't necessary to invent a
new language for every kind of methodology one currently is obsessed
with ;->

[2] In effect, many of the problems of correctness checking of
protocols and their implementations are similar to the kind of
property assertions that are necessary for most OO optimizations. In
this context, MOPs have been researched quite well, IIRC.

--
Pierre Mai <pm...@acm.org> http://home.pages.de/~trillian/
"One smaller motivation which, in part, stems from altruism is Microsoft-
bashing." [Microsoft memo, see http://www.opensource.org/halloween1.html]

Barry Margolin

unread,
Jan 19, 1999, 3:00:00 AM1/19/99
to
In article <87zp7fx...@orion.dent.isdn.cs.tu-berlin.de>,

Pierre Mai <pm...@acm.org> wrote:
>Barry Margolin <bar...@bbnplanet.com> writes:
>
>> Some languages allow you to attach preconditions and postconditions to
>> statements and functions, to try to capture more of the requirements of a
>> protocol. Some of these things may be statically checkable. AFAIK, this
>> type of stuff is still mostly in research languages, it hasn't reached the
>> mainstream much. And it's not clear to me how useful they really are;
>
>Well, the simple pre- and post-condition type of language has already
>"hit the mainstream", as in Bertrand Meyer's Eiffel. Matthias Hoelzl
>has even developed a prototypical implementation of
>"Design-By-Contract" for CLOS[1].

I carefully said "mostly", precisely because I suspected there were a small
number of languages like that, but which I didn't know offhand (I've heard
of Eiffel, but don't know much about it).

Harley Davis

unread,
Jan 19, 1999, 3:00:00 AM1/19/99
to

Erik Naggum wrote in message <31256652...@naggum.no>...

>* "Harley Davis" <spamless_davis@spamless_ilog.com>
>| I am not clear as to why Erik feels that C++ is especially flawed for
>| this, other than that its syntax is screwy as usual (no keywords for
>| abstract classes; need to define a pure virtual to make a base class
>| abstract; pure virtual function definition syntax is silly.) There is a
>| reasonable argument that C++ simply lets you specify too much or requires
>| you to overdesign your class hierarchies to take advantage of all the
>| static declarations you can make, but I'm not sure if this is what Erik
>| is getting at.
>
> the problem with C++ is that it pretends to give you something, but fails
> to follow up on it and actually deliver it. (this is not unlike most C++
> projects, but I digress.) in this case, a pure virtual function is a
> good idea that never got anywhere. it tries to answer the desire for a
> protocol between class designer and subclass implementor, but only the
> first subclass needs to implement it, yet if that one forgets it, some
> subclass of that class might need to, and despite all the "statically
> typed" nonsense, it doesn't really enforce any of this protocol. it's
> worse to pretend you give somebody something valuable than not to give
it.


Well, the alternatives are basically:

1. Do nothing to support abstract classes and interfaces. (CLOS)
2. Provide limited support at a very superficial level. (C++/Java)
3. Provide full support with arbitrarily complex
spec/implementation/semantics/limitations. (No language known to me.)

If there were at least good working research examples of option 3, it might
make sense to design new languages incorporating this feature - certainly we
all seem to agree that the concept is a good one. But there are none that
I'm aware of, so realistically only options 1 and 2 are available. The
question is really whether partial support of protocol definition is better
than no support at all.

I believe partial support is better, if only because it is still useful for
some level of consistency checking and writing a bunch of methods signalling
errors is a pain and does not state the desired semantics as clearly as a
special syntax for abstract classes.

I'm not sure that C++ promises more than it delivers - the language spec
says what the forms in question do, and a brief look at the literature does
not reveal extravagant claims about this feature other than that it allows
the programmer to specify abstract classes that are checked by the compiler
at instantiation time - perhaps not equal to the coming of the messiah as a
feature but still moderately useful.

However, I did find this little tidbit in "The Design and Evolution of C++"
by Stroustrup [p. 261]:

"The combination of abstract classes and virtual base classes was intended
to support a style of programming roughly corresponding to the mixin style
used in some Lisp systems. That is, an abstract base class defines an
interface, and several derived classes contribute to the implementation.
Each derived class (each mixin) contributes something to the complete class
(mix)."

(I think Stroustrup confused "derived class" with "virtual base class" in
the last two sentences; otherwise I can't make sense of his explanation.)

A more elaborate explanation can be found in the same book on pages 277-279,
where the major claims are that C++'s abstract classes support "a style of
design based on separating the specification of interfaces and
implementation."

Whether the claims for this feature are overblown depends, I suppose, on
your expectations going in; certainly nothing in the book says that C++ can
really do more than it does. So I would not accept without further evidence
that C++ promises something more valuable than it delivers.

To put all this in perspective, I don't think this one fairly small feature
in C++ does anything close to compensate for all its other flaws and
annoyances as a systems programming language, but I do think there is a
tendency to somewhat overstate the negative claims about the language among
Lispers.

BTW, I don't recall anyone mentioning in this discussion what I consider to
be the one huge advantage that Lisp-based object systems have over most
statically typed OO languages: The ability to define new methods and
generic functions outside of the scope of a class definition. There's
little I find more annoying in C++ and Java than having to define all of a
class's methods in the class definition, and nothing more annoying than when
I want to add some new protocol to a class I didn't write.

-- Harley

Paul Rudin

unread,
Jan 20, 1999, 3:00:00 AM1/20/99
to
t...@sevak.isi.edu (Thomas A. Russ) writes:


>
> This is a useful semantic concept when one is defining abstract
> superclasses, ie, classes which are not expected to be instantiated
> directly. This is typically done in order to have a common interface,
> or in situations where some portion of the functionality can be
> implemented by methods on the abstract class, but for which some
> additional concrete methods are required for proper functioning.
>
> This does not seem like an unreasonable request.
>

Is there a reason why this couldn't be implemented using a
non-standard metaclass for the classes for which this behaviour might
be desirable? (Apart, of course, from the work involved.)

Barry Margolin

unread,
Jan 20, 1999, 3:00:00 AM1/20/99
to
In article <783bkm$904$1...@news1-alterdial.uu.net>,

Harley Davis <spamless_davis@spamless_ilog.com> wrote:
>BTW, I don't recall anyone mentioning in this discussion what I consider to
>be the one huge advantage that Lisp-based object systems have over most
>statically typed OO languages: The ability to define new methods and
>generic functions outside of the scope of a class definition. There's
>little I find more annoying in C++ and Java than having to define all of a
>class's methods in the class definition, and nothing more annoying than when
>I want to add some new protocol to a class I didn't write.

There are many in the OO community who feel that these needs imply poor
design. The goal of information hiding doesn't mesh well with allowing
someone other than the class designer/implementor to define methods. The
theory is that he should be able to change the implementation of the class
without affecting any outside code (whether this includes subclasses varies
-- C++ has the "protected" designation for members that are visible to
immediate subclasses), so long as it continues to implement the same
protocol via the public interfaces.

To me, such attitudes seem consistent with those folks' preference for B&D
languages with lots of static type declarations, and there are times when
this philosophy is appropriate (large shops with hundreds of "coders"
implementing modules designed by a few "analysts"). Lisp is appropriate
for different circumstances, where the programmers are the designers, and
can take advantage of the freedom it provides (exploratory programming,
rapid prototyping). If you don't trust your programmers not to hang
themselves if you give them rope, you don't let them use Lisp.

On the other hand, C/C++ give them different pieces of rope. I find it
hard to believe that you don't trust to pass appropriate data types without
compile-time type checking, but you expect that same programmer to do
memory management properly. Lisp leaves the hard stuff (memory management)
to the implementation, while easy stuff like type checking is left for the
programmer to do if necessary.

Paul Rudin

unread,
Jan 27, 1999, 3:00:00 AM1/27/99
to
Paul Rudin <pa...@shodan.demon.co.uk> writes:

And thinking about it, this probably isn't much work involved. All
that's required would be a new metaclass based on standard-class with
one additionional slot to tell you whether or not it's virtual, and a
new before method on MAKE-INSTANCE specialised to this class that
complains when you try and make an instance if this slot is non-nil.
(+ a bit of syntactic sugar here and there...)


mark...@my-dejanews.com

unread,
Jan 27, 1999, 3:00:00 AM1/27/99
to
In article <m3yamp0...@shodan.demon.co.uk>,

This is essentially the approach we have taken.

A new class option :ABSTRACT may be specified. It defaults to nil but I find
it useful documentation to specify :ABSTRACT nil even though it is not
required.

We define a primary method on ALLOCATE-INSTANCE which errors when an attempt
is made to instantiate an abstract class.

I also use the accessor ABSTRACT in some cases e.g. when collecting a list of
slots in a class hierarchy there are some occassions when I want to filter out
slots belonging to abstract classes.

Mark

0 new messages