[comp.object] Inheritance explained... finaly...

jacob@gore.com (Jacob Gore) (01/05/90)

IEEE Spectrum, January 1990.  Cover topic: "Technology '90".  Under the
major heading "Systems software", there is an article called
"Object-oriented programming a hit" by Ted G. Lewis ("Expert Opinion").
Here's a passage from it:

	Perhaps the most powerful concept in object-oriented
	programming systems is inheritance.  Objects can be
	created by inheriting the properties of other objects,
	thus removing the need to write any code whatsoever!
	Suppose, for example, a program is to process complex
	numbers consisting of real and imaginary parts.  In a
	complex number, the real and imaginary parts behave like
	real numbers, so all of the operations (+, -, /, *,
	sqrt, sin, cos, etc.) can be inherited from the class of
	objects called REAL, instead of having to be written in
	code.  This has major impact on programmer productivity.

Sigh... maybe the April 1 issue was published early this year?

Jacob
--
Jacob Gore		Jacob@Gore.Com			boulder!gore!jacob

weiner@novavax.UUCP (Bob Weiner) (01/06/90)

In article <1130005@gore.com> jacob@gore.com (Jacob Gore) writes:

>  IEEE Spectrum, January 1990.  Cover topic: "Technology '90".  Under the
>  major heading "Systems software", there is an article called
>  "Object-oriented programming a hit" by Ted G. Lewis ("Expert Opinion").
>  Here's a passage from it:
>
>	   Perhaps the most powerful concept in object-oriented
>	   programming systems is inheritance.  Objects can be
>	   created by inheriting the properties of other objects,
>	   thus removing the need to write any code whatsoever!
>	   Suppose, for example, a program is to process complex
>	   numbers consisting of real and imaginary parts.  In a
>	   complex number, the real and imaginary parts behave like
>	   real numbers, so all of the operations (+, -, /, *,
>	   sqrt, sin, cos, etc.) can be inherited from the class of
>	   objects called REAL, instead of having to be written in
>	   code.  This has major impact on programmer productivity.

This passage when taken alone (since I have not read the article)
indicates that one can operate on 'complex numbers' as if they were
'real's.  Try again.  I'm sure the intent was to say that the 'real'
operators may be used to build the complex operators, which is true.
However once again the 'is-a' relation that is so important in many uses
of inheritance has been ignored.  It's a simple test that can be applied
with only a moment's thought: Is a 'complex number' a type of 'real'
number?  No.  It may be composed of a pair of real numbers and hence OO
techniques for composition should be applied rather than those for
inheritance.

With all the good examples in the world of 'is-a' and 'composed-of'
relations, why do people continually confuse the relations when
programming?
-- 
Bob Weiner, Motorola, Inc.,   USENET:  ...!gatech!uflorida!novavax!weiner
(407) 364-2087

jacob@gore.com (Jacob Gore) (01/06/90)

/ comp.object / weiner@novavax.UUCP (Bob Weiner) / Jan  5, 1990 /
> This passage when taken alone (since I have not read the article)
> indicates that one can operate on 'complex numbers' as if they were
> 'real's.  [...]  I'm sure the intent was to say that the 'real'
> operators may be used to build the complex operators, which is true.

Unfortunately, that's too generous.  The passage I presented is the
complete "treatise" of inheritance in the article.

Jacob

P.S.  The rest of the article is quite reasonable.  (Half of it does not
deal with O-O, but with other productivity improvements -- visualization,
CASE, interface building tools, etc.)

--
Jacob Gore		Jacob@Gore.Com			boulder!gore!jacob

jlg@lambda.UUCP (Jim Giles) (01/09/90)

From article <1130005@gore.com>, by jacob@gore.com (Jacob Gore):
> IEEE Spectrum, January 1990.  Cover topic: "Technology '90".  [...]
> "Object-oriented programming a hit" by Ted G. Lewis ("Expert Opinion").
> 
> 	Suppose, for example, a program is to process complex
> 	numbers consisting of real and imaginary parts.  In a
> 	complex number, the real and imaginary parts behave like
> 	real numbers, so all of the operations (+, -, /, *,
> 	sqrt, sin, cos, etc.) can be inherited from the class of
> 	objects called REAL, instead of having to be written in
> 	code.  This has major impact on programmer productivity.

In fact, I always use Complex numbers to demonstrate the some of the
problems which still plague Object-oriented languages.

For example, on my SmallTalk system the class heirarchy for numbers
is:

                        Object
                          |
                      Magnitude
                       /     \
                     Time   Number
                     Date   / | \
                           /  |  \
                     Integer  |  Float
                           Fraction

Obviously, I can't make Complex a subclass of number because complex
numbers aren't well ordered (and are, therefore, not Magnitudes).
Similarly, I can't rearrange the heirarchy an place Magnitude under
Number since I don't want the arithemtic operators defined on such
things as Time and Date.  Some Object-oriented languages are now
correcting this problem by allowing multiple inheritance - but not
without new problems.

Second example: Complex numbers show a weekness in Object-oriented
languages with respect to encapsulation.  This is because of the
concept of 'messages'.  For some applications, it is perfectly
reasonable (even desireable) to have _functions_ be asymmetrical
in their arguments.  But, for other applications, it is completely
unnatural.  In this case, it is necessary to modify the definitions
of the Integer, Float, and Fraction classes in order to implement
the expected mixed-mode operations on Complex.  This violates the
spirit of the Object-oriented language with respect to excapsulation.
Of course, many Object-oriented languages don't use message passing
syntax - and even some that do also offer overloading of operators
as a separate mechanism from message passing.  It should, after all,
be possible to define Complex as a single class without altering
any other classes to handle it.

In any case, I still have doubts about the future of Object-oriented
programming.  After all, except for inheritance, the whole concept
is a subset of what can be done with generic functions and overloading.
Maybe the correct approach is to add inheritance to a language which
has generic functions - it can't be THAT hard.

arshad@lfcs.ed.ac.uk (Arshad Mahmood) (01/09/90)

In article <1719@novavax.UUCP> weiner@novavax.UUCP (Bob Weiner) writes:
>In article <1130005@gore.com> jacob@gore.com (Jacob Gore) writes:
>

>This passage when taken alone (since I have not read the article)
>indicates that one can operate on 'complex numbers' as if they were
>'real's.  Try again.  

You seem to have missed the explanation at the bottom related to
April Fools day, even though I suspect the original article seems to
be quite serious (self sigh, self sob, etc).

A. Mahmood
LFCS
Edinburgh University
Scotland

jgk@osc.COM (Joe Keane) (01/10/90)

In article <14185@lambda.UUCP> jlg@lambda.UUCP (Jim Giles) writes:
>In any case, I still have doubts about the future of Object-oriented
>programming.  After all, except for inheritance, the whole concept
>is a subset of what can be done with generic functions and overloading.
>Maybe the correct approach is to add inheritance to a language which
>has generic functions - it can't be THAT hard.

Mr. Giles has a number of good points.  Let me quickly enumerate the bad
points of the object-oriented paradigm as it stands today.

Type system.  Single inheritance completely falls apart once you leave the
typical textbook `tree of types' example.  Multiple inheritance fixes a number
of these problems, but usually complicated things even further.  I have yet to
see a language which does multiple inheritance cleanly.  I can say more about
what they do wrong and how to do it right, but that would be a long post.

Multiple arguments.  Forcing the first (implicit) argument to be special is
bogus.  This is what you want sometimes, but many operations are fundamentally
operations on more than one object.  The way you add two numbers in Smalltalk
is to construct a message containing one of the numbers as an argument to the
addition operator, and send that thing to the other number.  I'll let the
reader decide if this makes sense, never mind if it is elegant.  Also,
polymorphism should not be indexed on the class of the first argument.

Side effects.  Object orientation includes a vague assumption that messages
modify their receivers, but not their parameters.  This needs to be made more
explicit and more general.  Note that the C++ `const' keyword is a start, but
it only addresses whether you modify the parameters or objects themselves, and
not what they may refer to or depend on.  Certainly any language which doesn't
have a concept of a pure function is deficient.

Notably, Smalltalk gets zero out of three on this evaluation.  This isn't
meant to be critical of the designers of Smalltalk, since you must take into
account that it was originally a string pattern matching language, and was
created a long time ago before the object-oriented paradigm existed as such.
However, i worry when i hear people touting Smalltalk as the state of the art.

khaw@parcplace.com (Mike Khaw) (01/10/90)

jlg@lambda.UUCP (Jim Giles) writes:

>In fact, I always use Complex numbers to demonstrate the some of the
>problems which still plague Object-oriented languages.

>For example, on my SmallTalk system the class heirarchy for numbers
>is:

>                        Object
>                          |
>                      Magnitude
>                       /     \
>                     Time   Number
>                     Date   / | \
>                           /  |  \
>                     Integer  |  Float
>                           Fraction

The current version of ParcPlace's Smalltalk-80 (version 2.5) provides
a Complex class as a file-in.  The class hierarchy then looks like:

	Object
		Magnitude
			ArithmeticValue
				Number
					Fraction
					Integer
						LargePositiveInteger
						LargeNegativeInteger
						SmallInteger
					LimitedPrecisionReal
						Float
				Point
				Complex

>Obviously, I can't make Complex a subclass of number because complex
>numbers aren't well ordered (and are, therefore, not Magnitudes).
>Similarly, I can't rearrange the heirarchy an place Magnitude under
>Number since I don't want the arithemtic operators defined on such
>things as Time and Date.  Some Object-oriented languages are now
>correcting this problem by allowing multiple inheritance - but not
>without new problems.

ParcPlace's implementation of Complex makes use of version 2.5's
exception handling mechanism to raise an exception on attempts to send
comparison messages (e.g., "aComplex < bComplex").

As for Time and Date, I can imagine wanting to do arithmetic on
instances of them (in fact, I have an old Casio calculator that does
arithmetic on times and dates).

>Second example: Complex numbers show a weekness in Object-oriented
>languages with respect to encapsulation.  This is because of the
>concept of 'messages'.  For some applications, it is perfectly
>reasonable (even desireable) to have _functions_ be asymmetrical
>in their arguments.  But, for other applications, it is completely
>unnatural.  In this case, it is necessary to modify the definitions
>of the Integer, Float, and Fraction classes in order to implement
>the expected mixed-mode operations on Complex.  This violates the
>spirit of the Object-oriented language with respect to excapsulation.
>Of course, many Object-oriented languages don't use message passing
>syntax - and even some that do also offer overloading of operators
>as a separate mechanism from message passing.  It should, after all,
>be possible to define Complex as a single class without altering
>any other classes to handle it.

Double dispatching takes care of making binary operations commutative;
e.g., "2 + aComplex" conceptually results in the "+" method in
SmallInteger "failing", then turning things around and sending
"aComplex + self", since the "+" method of Complex implements adding
integers to complex numbers.  SmallInteger doesn't have to know
anything about Complex, Float, etc., it just has to know that the
argument to its "+" method is not something that it can handle, and
do the double dispatching.
-- 
Mike Khaw
ParcPlace Systems, Inc., 1550 Plymouth St., Mountain View, CA 94043
Domain=khaw@parcplace.com, UUCP=...!{uunet,sun,decwrl}!parcplace!khaw

jlg@lambda.UUCP (Jim Giles) (01/11/90)

From article <661@parcplace.com>, by khaw@parcplace.com (Mike Khaw):
> [...] 
> ParcPlace's implementation of Complex makes use of version 2.5's
> exception handling mechanism to raise an exception on attempts to send
> comparison messages (e.g., "aComplex < bComplex").

Which is exactly what I was complaining about.  Whoever wrote the
definition of the Complex class had to redefine the relational operators
on them in order to generate these errors.  With multiple inheritance,
he would not have had to concern himself with such things (which he
shouldn't).  Complex numbers aren't ordered - so the implementor of
them shouldn't have to worry about relational operators AT ALL.

> [...] 
> As for Time and Date, I can imagine wanting to do arithmetic on
> instances of them (in fact, I have an old Casio calculator that does
> arithmetic on times and dates).

Not full arithmetic though.  You want to subtract two dates or add
a number of days to a date, for example.  You probably never want
to take the square root of a date or multiply two dates.  That's
why Time and Date are subclasses of Magnitude but not Number (usually).

> [...]
> Double dispatching takes care of making binary operations commutative;
> e.g., "2 + aComplex" conceptually results in the "+" method in
> SmallInteger "failing", then turning things around and sending
> "aComplex + self", since the "+" method of Complex implements adding
> integers to complex numbers.  SmallInteger doesn't have to know
> anything about Complex, Float, etc., it just has to know that the
> argument to its "+" method is not something that it can handle, and
> do the double dispatching.

Yes, double dispatching will work for commutative operators.  This 
is an inefficient and clumsy solution which happens to work (and leave
_some_ people satisfied).  However, a lot of operators aren't commutative.
What do you do with "2 / aComplex"?  When I added Complex to SmallTalk,
it _had_ special cases for Float and Fraction in both the Integer ans
SmallInteger classes.  I switched it to do double dispatching by defining
a new method in the Float, Fraction, and Complex classes called 
reverseDivide:.  So, now when the divide method fails in a given
class, I send the reverseDivide: message to the other operand.
(So, "2 / aComplex" becomes "aComplex reverseDivide: 2".)  This is
also inefficient and inelegant.  None of this would be required if
the arithmetic operators weren't asymetrical in their arguments.

As I originally said: the asymetrical nature of message passing is
quite appropriate for some data types but not for others.  It's
unfortunate to be forced to choose instead of being given a language
which allows a choice between the two mechanisms.

J. Giles

franka@mentor.com (Frank A. Adrian) (01/11/90)

In article <1831@osc.COM> jgk@osc.COM (Joe Keane) writes:
:In article <14185@lambda.UUCP> jlg@lambda.UUCP (Jim Giles) writes:
:>In any case, I still have doubts about the future of Object-oriented
:>programming.  After all, except for inheritance, the whole concept
:>is a subset of what can be done with generic functions and overloading.
:>Maybe the correct approach is to add inheritance to a language which
:>has generic functions - it can't be THAT hard.
:
:Mr. Giles has a number of good points.  Let me quickly enumerate the bad
:points of the object-oriented paradigm as it stands today.
:
:Type system...
:Multiple inheritance fixes a number
:of these problems, but usually complicated things even further.  I have yet to
:see a language which does multiple inheritance cleanly.
:
:Multiple arguments.  Forcing the first (implicit) argument to be special is
:bogus...
:Polymorphism should not be indexed on the class of the first argument.
:
:Side effects.  Object orientation includes a vague assumption that messages
:modify their receivers, but not their parameters.

So what's the problem?  You have a language that does somewhat better on ALL
of these - CLOS.  Not only does it have multimethods and generic functions
(See Mr. Giles' suggestion and the stuff about multiple arguments), it
also has a fairly controllable multiple inheritance system (although I agree,
it sometimes isn't exactly clean - is that simply a problem with describing
real world multiple roles which objects can play?).  Side effects can be
performed on message parameters (or not).

All in all, it seems like a pretty good language (especially if you like LISP
in the first place :-).

Just my own somewhat grumpy opinion...

Frank A. Adrian
Mentor Graphics, Inc.
franka@mntgfx.com

johnson@p.cs.uiuc.edu (01/13/90)

>jgl@lambda.UUCP (J. Giles) writes
>Yes, double dispatching will work for commutative operators.  This 
>is an inefficient and clumsy solution which happens to work (and leave
>_some_ people satisfied).  However, a lot of operators aren't commutative.

>As I originally said: the asymetrical nature of message passing is
>quite appropriate for some data types but not for others.  It's
>unfortunate to be forced to choose instead of being given a language
>which allows a choice between the two mechanisms.

I agree that the asymetrical nature of message passing is not appropriate
for all data types, but I disagree with everything else.  Double dispatching
is actually quite efficient.  In my opinion, it is also quite elegant.  
Moreover, it works best with noncommunitive operators.  Double dispatching
is a good way to implement those data types that require symetrical
method lookup.

The major problem with double dispatching in Smalltalk is that the
programming environment does not support it.  Kurt Hebel and I have a 
paper in an upcoming issue of JOOP that describes a double dispatching
browser that makes it very easy to write code that uses double dispatching.
It automatically writes all the little methods for message forwarding, 
automates inheritance along the lines of the class of the second object,
and in general makes the implementation of the arithmetic classes in
Smalltalk very symetrical.  Kurt completely reimplemented the Smalltalk
arithmetic classes, adding matrices, vectors, functions, etc.  I like
double dispatching because my Smalltalk compiler can produce very efficient
code for arithmetic that is implemented using it, but Kurt is very happy
with it in Smalltalk-80.

The resulting system is very similar to CLOS generic functions from the
programmer's point of view.

Ralph Johnson

johnson@p.cs.uiuc.edu (01/13/90)

I disagree with almost everything that jgk@osc.COM said in his message.

>Type system.
He then goes on to talk about inheritance.  (I guess that I agree that
I have yet to see a language which does multiple inheritance cleanly,
though Trellis/Owl is pretty good.)  His basic mistake is to confuse
inheritance with types.  This is understandable, because lots of other
people do too.  However, the problems with single inheritance that he
alludes to are easily fixed by a type conformance algorithm that ignores
inheritance.  Witness Emerald, which does not have inheritance at all.

>Multiple arguments.
This is a bogus argument.  Double dispatching works fine for those cases
where you really want to choose a method based on the class of several
objects.  Most of the time you don't, and messaging works fine.

>Side effects.
As a matter of fact, I guess I agree with the basic assumption that it
would be good to be able to reason about side effects better, but this
is not particularly related to object-oriented programming, either in the
need or in the solution.

I disagree that object orientation includes a vague assumption that messages
modify their receivers, but not their parameters.  It is common for Smalltalk
methods to change the state of their parameters and also for them to not
change any state at all.  The statement "Certainly any language which doesn't
have a concept of a pure function is deficient" is a little odd, since none
of the popular languages support this concept as well as they should.  The
only language I've seen that lets procedures combine arguments that can be
changed with those that cannot in a way that I think is flexible enough is FX.
I'd appreciate any other pointers to work in this area.

Ralph Johnson