[comp.lang.c++] Current O-O Languages as Software E

johnson@p.cs.uiuc.edu (11/15/88)

bs@alice.UUCP (Bjarne Stroustrup) answered 
coggins@retina.cs.unc.edu (Dr. James Coggins) with

>Alternatively we could use the trick of never allocating class objects on
>the stack. This is the strategy of Simula and most of its decendents.
>The snag is that if we did that we would incur an overhead of two memory
>management operations per function call and the cost of indirecting every
>access to a class object. Measurements on Simula indicates that this cost
>is at least a factor of 2 in run-time. Most of Simula's decendents pay
>an even higher price. Accepting this overhead would imply giving up large
>application areas to C, assembler, and Fortran. C++ was specifically designed
>to preserve efficiency in this area. The apparant cost is recompilation time.

I don't believe this.  The reason is that the mature C++ systems I have
seen allocate very few "class objects" (why aren't these just "objects"?)
on the stack.  Almost all are allocated dynamically.  Procedure calling
takes a fairly small percentage of time, especially when low-level
functions are in-lined, so the factor of 2 seems way out of line. 
The cost of indirect access to these objects is repaid by the elimination
of copying them later.

> > 2. Smalltalk-like languages are better tools for developing small
> > programs because of their massive built-in class libraries and their
> > more flexible (later, dynamic) binding which allows polymorphic types. 

>Again I must disagree. Of course you can throw a small Smalltalk program
>together to do many things that would be painful to build from scratch in
>C++. However, the massive libraries and the wonderful program development
>environment of Smalltalk is not something that C++ lacks because of some
>inherent defect. Rather, Smalltalk is about 10 years older than C++ and
>have had something like 100 times more effort and resources lavished on
>its environment and libraries.

I understood Jim Coggins to be saying that he thought that C++ DID have an
inherent defect that prevented the large class libraries from being created.
I don't really understand what it is about C++ that he thinks causes this,
nor am I convinced that C++ has any fatal errors in this regard, but that
was his claim.

My experience is that Smalltalk is better for prototyping a design, but
that once it is finished it doesn't matter that much which language you use.

The Smalltalk's that are available now have class libraries that are little
changed from what was available in 1982.  Thus, it isn't fair to say that
Smalltalk is 10 years older than C++.  My understanding is that there were
only a dozen or so people at Xerox PARC who worked on the Smalltalk 
programming environment directly (other people were just users).  When
you count all the people in universities who are using C++, the number
of man-years invested in C++ can't possibly be an order of magnitude less
than for Smalltalk, and in a year it will be an order of magnitude more.
Everywhere I go people are switching to C++, and there are groups of more
than a hundred people using C++.  There is nothing at all like this for
Smalltalk.  However, it will take more than a year or two for the
C++ programming environment to surpass that of Smalltalk.

The reason that Smalltalk programming environments are so good is the same
as why Lisp programming environments are so good.  Programmers can access
contexts and classes from inside Smalltalk so they can easily write
debuggers and browsers.  A similar system for C++ will require a lot of
interaction with the operating system, which will be more complicated and
less portable.  Thus, making a nice programming environment for C++ is
a harder job than making one for Smalltalk, so it will take longer.

>There is absolutely no basis for this conjecture and the absense of really
>large projects successfully developed and supported in a Smalltalk-like
>language is a contraindication. After 16 years of Smalltalk variants and
>offshoots we should not be conjecturing on this point. Smalltalk has a
>string of spectacular successes to its credit, but as far as I'm aware
>large scale systems development isn't among them. Nor was it mentioned
>among the things Smalltalk was supposed to be especially good at.

I suppose that it depends on what you mean by "large".  Smalltalk itself
is pretty large.  The Analyst is twice as large as Smalltalk.  There is
a VLSI design system at Tektronix that takes 12 or 16 megabytes (I forget
which).  I doubt if there have been many C++ systems of this size yet.
Smalltalk certainly has some faults when it comes to building large
systems, primarily in its support for mulitperson projects, but I think that
Dr. Coggins was speaking of "Smalltalk-like" languages (whatever that
means) and not Smalltalk itself.

I am a great fan of type systems, primarily because I am a fan of good
optimizing compilers.  The reason that Smalltalk is slow is primarily 
because it doesn't have an optimizing compiler.  Indirection, method-lookup,
garbage collection, etc. are all just excuses.  Of course, C++ can be 
efficient without a great optimizing compiler, though a good compiler 
certainly helps.

Type systems are also important in ensuring that components are used
correctly.  This is especially important when components are used by
one group and implemented by another.

Type information also makes it much easier to read code.  O-o programming
increases code reading, so this is important.

My type system for Smalltalk is much more flexible than the one for C++.
I am curious about Dr. Coggins's explanation of why C++ is not as good
for reuse, and wonder how types fit in to it.

Ralph Johnson

johnson@p.cs.uiuc.edu (11/19/88)

Claim:  (by some unknown person)
  dynamic binding at run time allowed architecture and implementation
  to be more independent of each other in practice, therefore the Smalltalk
  model would be better for large-system development than the Simula model.

I agree with the first part of the statement, but C++ provides dynamic
binding at run time, so the last half is a non sequitur.

>If you prototype using Smalltalk, you adopt the Smalltalk object hierarchy
>as your architectural tools/metaphors/thought patterns. You also adopt
>the underlying implementations, but they can be thrown out or redone
>later.  By adopting the Smalltalk environment, you might be
>starting off your architectural design effort by putting on a
>straitjacket and blindfold.  Anything works for small projects.  For
>large projects the fit of mental models, architecture, and language
>abstractions needs to be tighter in order to manage and control the
>project.  I believe that this fit can be made tighter by designing
>abstractions (classes) to fit the project, not twisting the mental
>model to fit a predefined hierarchy.  This is the '80's version of the
>top-down heuristic: 
>          Let the implementation conform to the conception. 

There seem to be several misunderstandings of Smalltalk here.  Smalltalk
certainly does not force anyone to use existing classes.  People use them
because they are well designed.  People use classes for control structures,
arithmetic, data structures (collections), and user interface design.  There
really aren't many application specific classes.  Any large project will
require the creation of many new classes, i.e. you will have to invent
your own abstractions and implement them.

Most of these classes are universally needed and well done.  The main
exceptions are the graphics and user interface classes, which were very
well designed for 1980, but are showing their age.  However, people don't
seem to see much need in redesigning the collection classes.

It seems to me that you are arguing against reuse.  I find that hard to
believe.  It is very hard to write reusable code, and trying to reuse
code that is not reusable is no fun.  However, it is possible for code
to be reusable, and Smalltalk is an example.  Smalltalk programmers reuse
code because that is the easiest way to develop a high-quality product.

In my opinion, if you aren't interested in reuse then you shouldn't
bother with object-oriented programming.  However, I'll assume that
are you interested in reuse.

As a project grows, class hierarchies get extended.  This usually results
in bad class hierarchies, so eventually they get reorganized.  One could
claim that you should just have designed them right to begin with, but
hind-sight is always so wonderful.  The problem is how to get hind-sight,
and the solution is to go build something (wrong).  I claim that no good
designer is going to find a class hierarchy a straight-jacket, because if
it is not working then it will be changed.

There are several situations where reuse can cause problems.  One is where
you program instead of thinking.  Thinking is always a good idea, and I
push prototyping as an aid to thinking, not as a replacement to it.  Another
is where you start with a bad package and so have to spend a lot of time
fixing it.  If there is a better package available, this is a complete
waste of time.  However, if you are using the only one available then
reading somebody else's code prevents you from learning by an even harder way.
(I am assuming that the writer of the package that you are using is at least
as good a designer as you are, and that the problems with the package are due
to narrowness of vision, and not incompetance.)  

Although prototyping without thinking can cause problems, thinking without
prototyping is more common and also causes problems.  I have seen people
debate for weeks about some point that could be solved in a couple of days
by experimentation.  It is a lot easier to debate about an algorithm once it
is written down, at least people are arguing about the same thing.

There should be much, much, much more reuse in software systems.  We don't
keep inventing new abstractions for arithmetic, why should we be doing it
for everything else?  It is clear that it takes a long time to come up
with good abstractions, so new areas, like user interface design, go
through a lot of fermet until they mature.  However, eventually we figure
out what we are doing and then we can quit throwing out our old software
can can just reuse it for awhile.  There are lots of areas of CS that are
well enough understood that we should be able to standardize them.  This
includes many areas in operating systems (scheduling, file systems), areas
in business (general ledger, receivables, payables), and a lot of graphics.
Then people can write books about these abstractions and we can just use
them.

In a couple of years C++ will have just as large a class library as
Smalltalk, maybe larger.  Lots of companies are already developing their
own.  Nobody will force you to use them, but the high-quality libraries
will have lots of users.

>>My type system for Smalltalk is much more flexible than the one for C++.
>>I am curious about Dr. Coggins's explanation of why C++ is not as good
>>for reuse, and wonder how types fit in to it.

>The question is whether the flexibility in the type system afforded by
>late dynamic binding is ultimately an advantage (by virtue of its
>additional support for abstracting architectures independent of
>implementations) or ultimately a disadvantage (by delaying detection
>of errors and in fact not detecting as errors some things that in fact
>are errors). 

One of the purpose of a type system is to detect errors at compile-time.
A type system for an object-oriented language, for example, should
prevent any "message not understood" errors at run-time.  I don't see
why late binding itself prevents the detection of errors.  As I said
earlier, C++ virtual functions provide late binding, and the type system
for C++ ensures that an object understands the messages sent to it.

johnson@p.cs.uiuc.edu (11/22/88)

Let me make one thing clear: I am a Smalltalk programmer.  Smalltalk is
my favorite language and I consider C++ definitely inferior.  I have been
defending C++ (and will continue in this article) so I wanted to make
that clear.  I think that my comments are reasonably representative of
the Smalltalk community.

Smalltalk certainly has a more dynamic programming environment than C++,
but the language itself is not much more dynamic.  If you are trying to
send a message to an object that does not understand it then you will
get a run-time error.  If you want a procedure to work with any object
that understands Foo and Fee then you make an abstract class FooFeeable
and use multiple inheritance to make every class understand it that might
be an argument to that procedure.  (In my opinion this is the main reason
for multiple inheritance.)  Very few Smalltalk programs take advantage
of the fact that they can generate code on the fly.  Smalltalk programs
do sometimes check the class of objects.  This is often a sign of poor
design, but try defining a general equality procedure without it.  This
is the way I can see in which C++ is significantly less dynamic than
Smalltalk.  The major problem with C++ is that it is too much like C,
but then this is its major strength, too.

As I said before, it is the programing environment of Smalltalk that is
more dynamic than that of C++, not the languages themselves.  The
languages certainly have an impact on the environments.  Smalltalk makes
it easy to write browsers, debuggers, inspectors, and the like.  However,
given enough work, it should be possible to do it for C++.

I commented...
>>Smalltalk certainly does not force anyone to use existing classes.  
>>People use them because they are well designed. 

Dr. James Coggins commented...
>That's a correct Party Line response that is meaningless in practice.
>This is exactly the kind of conventional wisdom that should be
>examined more carefully.  Each of these classes in the Smalltalk
>architecture comes with an implementation bound up with it. Part of
>the price you pay for using Smalltalk is the tacit adoption of those
>implementations along with the nice architecture.

The important feature of the Collection hierarchy is the protocol,
i.e. do:, select:, collect:, add:, etc.  I usually start off a program
using OrderedCollection, Dictionary or Set.  Once I figure out what I
am doing I often discover that these are too inefficient.  Then I will
write a specialized class to implement the exact kind of Collection
that I need.  The advantage of using a standard protocol is that
  1) readers of my program will know what the new data structure does
     without reading the code
  2) I don't design an efficient data structure until I know that I need it
  3) I might luck out and be able to reuse the specialized class.
Most times that I use a Collection its efficiency is unimportant, so
I never have to reconsider what I've done.

I said
>>I claim that no good designer is going to find a class hierarchy a 
>>straight-jacket, because if it is not working then it will be changed.

Dr. James Coggins said
>I am a good designer (only a fair implementer, though), and I found the 
>Smalltalk hierarchy restricting when I needed to implement large objects
>like images and 3-D graphics models.  I wasted time trying to fit what
>I needed into what Smalltalk provided. 

What you said was that you found an application where the particular
Smalltalk implementations were not what you needed.  So build another one!
Maybe you were saying that the abstractions provided by Collection were
inappropriate for your problem.  This is a more fundamental design error,
and one that is not the fault of Collection.  In other words, you should
have realized that you needed a different abstraction.  Of course,
hind-sight is 20/20, the problem is how to get hind-sight.

I said
>>There should be much, much, much more reuse in software systems.  We don't
>>keep inventing new abstractions for arithmetic, why should we be doing it
>>for everything else?  

Dr. James Coggins said
>Because there is no "implementation" for arithmetic, of course!
>Computer stuff is bound to implementations requiring engineering
>decisions concerning tradeoffs of time/space, compilation
>effort/run-time support effort, etc.

Note that I said "abstractions" not code.  The abstractions are much
more important than the code.  If you made a mistake in the implementation
of a class then you can just write another one.  If you designed the
interface wrong then you will not only have to reimplement the class
but every client of the class.  Every language that I have used gives
the same definition for + on integers.  I want the same thing to be
true of sets. 

coggins@retina.cs.unc.edu (Dr. James Coggins) (11/23/88)

In article <77300017@p.cs.uiuc.edu> johnson@p.cs.uiuc.edu writes:
>
>As I said before, it is the programing environment of Smalltalk that is
>more dynamic than that of C++, not the languages themselves. 

Certainly this is true, but the language differences are still significant.
Errors Smalltalk picks up at run-time are compile-time errors in C++.
Bjarne Stroustrup recently argued in this newsgroup that early detection 
of errors is a principal advantage of C++ over Smalltalk for large-scale 
real software development.

[I'm deleting a good description of how to do rapid prototyping in Smalltalk.]

>What you said was that you found an application where the particular
>Smalltalk implementations were not what you needed.  So build another one!

I did. In C++.  Where the nature of the implementation was under my control.
Where I could fit the implementation to the abstraction instead of the
other way around.

>Maybe you were saying that the abstractions provided by Collection were
>inappropriate for your problem.  This is a more fundamental design error,
>and one that is not the fault of Collection.

I saw very quickly that Collection was not what I wanted and neither
was anything else in the Smalltalk world. (I said I'm a GOOD
designer!) It was disappointing to spend the time to learn the
Smalltalk model and find it inappropriate.  

> 
>johnson said 
>There should be much, much, much more reuse in software systems.  We don't
>keep inventing new abstractions for arithmetic, why should we be doing it 
>for everything else?  
> 
>Dr. James Coggins said (this is amended somewhat from the original) 
>Because there is no "implementation" for arithmetic, of course!  
>As soon as abstractions are made computer-usable, they are bound to
>implementations requiring engineering decisions concerning
>tradeoffs of time/space, compilation effort/run-time support effort,
>etc.
We have no mechanism available for specifying the nature of
abstractions that is not bound up with some implementation. 
For rapid prototyping, something like Smalltalk provides default
implementations, and you correctly described how those should be
modified in response to real needs as a project develops.

But at some point you need to build the Product, not a prototype of
it.  From the prototype you might know how you would like the user
interface to work, what critical algorithms are required, and you
might know something about performance problems.  But if you have
expressed the prototype in a system of Smalltalk classes that is not a
good fit between the essential abstractions and implementations
thereof, then the prototype tells you less about the Product than you
thought. 

This all boils down to something I wrote once called "Object-Oriented
Design Considered Harmful".  The most negative effect of OOD is that
it encourages bottom-up design.  Top-down design remains the best
approach (though bottom-up realization makes lots of sense a lot of
the time). Huge class libraries might save some coding and typing
time, but we haven't even begun to fabricate the "silver bullet" until
we find an effective mechanism for divorcing the architecture of a
system from its implementation. Then we might be able to adopt and
reuse architectures like large class hierarchies conforming to
different conceptions of diverse problem domains while also
interchanging implementations. Then maybe the "software IC market"
will be less of a pipe dream and we will really be able to "Let the
implementation conform to the conception."

---------------------------------------------------------------------
Dr. James M. Coggins          coggins@cs.unc.edu
Computer Science Department   
UNC-Chapel Hill               Member of the Dreamer Fithp
Chapel Hill, NC 27514-3175    
---------------------------------------------------------------------

djk@murtoa (David Keegle) (11/23/88)

From article <77300017@p.cs.uiuc.edu>, by johnson@p.cs.uiuc.edu:
] I said
]>>There should be much, much, much more reuse in software systems.  We don't
]>>keep inventing new abstractions for arithmetic, why should we be doing it
]>>for everything else?  

Hear, hear!

] Dr. James Coggins said
]>Because there is no "implementation" for arithmetic, of course!
]>Computer stuff is bound to implementations requiring engineering
]>decisions concerning tradeoffs of time/space, compilation
]>effort/run-time support effort, etc.
] 
] Note that I said "abstractions" not code.  The abstractions are much
] more important than the code.  If you made a mistake in the implementation
] of a class then you can just write another one.  If you designed the
] interface wrong then you will not only have to reimplement the class
] but every client of the class.  Every language that I have used gives
] the same definition for + on integers.  I want the same thing to be
] true of sets. 

OK, what is ``maxint + maxint'' (your syntax may vary etc.) then? :-) ;-)

			David Keegel	(djk@munnari.oz)
                 "A tautology is something which is tautologous."