[comp.object] inheritance and `type loss'

sra@ecs.soton.ac.uk (Stephen Adams) (07/10/90)

This might be a naive question, but it has been puzzling me
for a while.  Consider the class integer.  This class
supports two methods:

	+       which takes another integer and returns the sum
	image   which returns a string denoting the integer
		as a decimal number

so 2+4 -> 6  and  (6).image -> "6"

Suppose I create a subclass called roman.  I provide a new
method called image which returns roman numerals e.g.
roman_6.image -> "VI".  The + operation is inherited from
integers.

I declare the following variable and initialize it

	V:roman := make a roman with the value 5

I have two questions:

(a)  what should   (V+V).image  yield?  "10" or "X"?
		   (V+1).image          "6" or "VI"?
		   (1+V).image          "6" or "VI" or error (in +)?

(b)  what do various object oriented systems actually do?


If V+V is not a roman (but an integer, 10) then some
information has been lost.  This might be reasonable but it
does seem surprising.
--
Stephen Adams                        S.Adams@uk.ac.soton.ecs (JANET)
Computer Science                     S.Adams@ecs.soton.ac.uk (Bitnet)
Southampton S09 5NH, UK              S.Adams@sot-ecs.uucp    (uucp)

jacob@gore.com (Jacob Gore) (07/11/90)

/ comp.object / sra@ecs.soton.ac.uk (Stephen Adams) / Jul 10, 1990 /

The key is that roman is a subclass of integer, and not vice versa.

>(a) what should   (V+V).image  yield?  "10" or "X"?

"X", because a roman + a roman -> a roman

> 		   (V+1).image          "6" or "VI"?

"VI", because a roman + an integer -> a roman
Why?  A roman is an integer, so any place an integer can be used, a roman
can be used just as well.  Thus, "a roman + an integer" automatically gets
the type of "a roman + a roman".

>		   (1+V).image          "6" or "VI" or error (in +)?

"6", according to your definition of +.  Not an error because V is a roman
and hence an integer, and + is defined for two integers.


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

bmcphers@alias.UUCP (Brent McPherson) (07/11/90)

In article <1130012@gore.com> jacob@gore.com (Jacob Gore) writes:
>/ comp.object / sra@ecs.soton.ac.uk (Stephen Adams) / Jul 10, 1990 /
>
>The key is that roman is a subclass of integer, and not vice versa.
>
> [stuff omitted...]
>
>> 		   (V+1).image          "6" or "VI"?
>
>"VI", because a roman + an integer -> a roman
>Why?  A roman is an integer, so any place an integer can be used, a roman
>can be used just as well.  Thus, "a roman + an integer" automatically gets
>the type of "a roman + a roman".
>

I agree with you on the first and last case but not the second.
The second case is incorrect since you cannot automatically convert
from a base class to a derived class.

Here is a simple example that illustrates the point.

Dog is a subclass of mammal (therefore a dog is a mammal).
A dog is a mammal, so any place a mammal can be used,
a dog can be used just as well.

The fundamental flaw in the logic is that every mammal is a dog
when only the reverse is true.

The solution will vary depending on the facilities offered by your
favorite OOPS language. The first solution is to provide an overloaded
'+' operator that takes an integer as a second argument (to explicity
define what happens in that case). The second solution (allowed by C++)
is to define a conversion method to allow the compiler to convert
the roman to an integer before performing the addition.

ie. C++ first looks for a method/routine where the types of the
arguments exactly match the formal parameters.  If an exact match is not
found, then the compiler will look for conversions to apply to the
argument in order to generate an exact match. Finally, if all else fails,
an error message is generated.

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

--
Brent McPherson                       bmcphers%alias@csri.toronto.edu

jimad@microsoft.UUCP (Jim ADCOCK) (07/12/90)

In article <SRA.90Jul10155606@alonzo.ecs.soton.ac.uk> sra@ecs.soton.ac.uk (Stephen Adams) writes:

>(a)  what should   (V+V).image  yield?  "10" or "X"?
>		   (V+1).image          "6" or "VI"?
>		   (1+V).image          "6" or "VI" or error (in +)?
>
>(b)  what do various object oriented systems actually do?
>

C++ provides are variety of ways to allow a programmer to either return
an int or a roman in the above example.  Generalizing the problem to
full polymorphic behavior is quite doable -- but ugly, requiring a double
dispatch.  Say in general, one wants:

	aDog = aDog + aDog;

but in specific one wants:

	aPoodle = aPoodleF + aPoodleM;
	aCockerSpaniel = aCockerSpanielF + aCockerSpanielM;
	aCockapoo = aPoodleF + aCockerSpanielM;
	aCockapoo = aCockerSpanielF + aPoodleM;

--So one can end up specifying a lot of flavors of "+" to get exactly the
behavior one wants.  [I'm using inter-breeding as a metaphor -- I'm not
suggesting the above is a good overloading of "+"]

--- 

void image(int i) { printf("%d\n", i); }

class roman1
{
protected: int i;
public:
    roman1(int ii) : i(ii) {}
    operator int() { return i; }
// a simplified version of roman numerals -- works for small positive ints :-)
    friend void image(roman1 r) { while (r.i--) printf("I"); printf("\n"); }
};

class roman2 : public roman1
{
public:
    roman2(int i) : roman1(i) {}
    friend roman2 operator+(int i, roman2 r2) { return i + r2.i; }
};

main()
{
    int anInt = 1;
    roman1 aRoman1 = 2;
    image(anInt + aRoman1); // prints "3"
    roman2 aRoman2 = 2;
    image(anInt + aRoman2); // prints "III"
    return 0;
}

gilstrap@swbatl.sbc.com (Brian Gilstrap - UCI - 5-3929) (07/12/90)

Jacob Gore's comments have one '>'
Stephen Adams' have two '>'s

[1]
>>(a) what should   (V+V).image  yield?  "10" or "X"?
>"X", because a roman + a roman -> a roman

[2]
>> 		   (V+1).image          "6" or "VI"?
>"VI", because a roman + an integer -> a roman
>Why?  A roman is an integer, so any place an integer can be used, a roman
>can be used just as well.  Thus, "a roman + an integer" automatically gets
>the type of "a roman + a roman".

This statement doesn't make sense to me.  (IMHO) What's really going on here is
simply a precedence rule: "In a binary operator, if the second argument can
be converted to the same type/class as that of the first, then use the type
of the first argument as the default result type."

[3]
>>		   (1+V).image          "6" or "VI" or error (in +)?
>"6", according to your definition of +.  Not an error because V is a roman
>and hence an integer, and + is defined for two integers.

Again, the same precedence rule.

I could easily see a language where the default was

	convert the Roman to an Integer no matter if it's first or second
	(least common denominator)

OR
	convert the Integer to an Roman no matter if it's first or second
	(I guess you could call this "specialization of results")

Just because C++ approaches things the way described above doesn't mean it
has to be that way.

By the way, this isn't meant to be flammage.  Please correct me if I've
missed something.

Brian R. Gilstrap
gilstrap@swbatl.sbc.com
gilstrap@swbatl.uucp
...!{texbell,uunet}!swbatl!gilstrap

pkr@media01.UUCP (Peter Kriens) (07/12/90)

Roman nummerals and integers, What is the result of 1 + V and 
V + 1?

Isn't the fact that  this problem is exactlt what Smalltalk does
with coercion? In Smalltalk each class under Number gets a priority.
When you are doing arithmetic with any number, it will compare
the numbers and coerce the lower number into a new type of the
higher number. This way the class itself can decide what the
result is of V+1.

So if V would return a higher priority than the integer 1, the
1 would be coerced into a roman nummeral and then the add message
would be send again. 

It is some time ago that I looked into this matters, but as
far as I remember this is the way it works in Smalltalk and
CO2.

Peter Kriens

jacob@gore.com (Jacob Gore) (07/12/90)

/ comp.object / sra@ecs.soton.ac.uk (Stephen Adams) / Jul 10, 1990 /

Let me try this again, since what I said before makes no sense to me
either...

I'm only talking about the effect of inheritance on the problem, so I
assume that the language does not use double dispatching, upward coersion,
or overloading (i.e., there is only one "+" operation per class).

>	+       which takes another integer and returns the sum
>	image   which returns a string denoting the integer
>		as a decimal number
>
> so 2+4 -> 6  and  (6).image -> "6"

I also assume that "2+4" means "(2).add(4)".

Now, since the compiler does no implicit type conversion, the type of the
result and the operand of each operation depends on the programmer of that
operation, rather than by the compiler.  So, here's how I'd do it:

> (a)  what should   (V+V).image  yield?  "10" or "X"?

I would define "+" as:

	+ : Like Self x Like Self --> Like Self

so "V+V" returns a roman, hence (V+V).image returns "X".

>		   (V+1).image          "6" or "VI"?

With my definition of "+", this would be an error.  "Like Self" is Roman
for V, and an Integer is not an acceptable replacement for a Roman.

(Of course, if this was a common enough operation, "+" would be better
defined as
	+: Like Self x Integer --> Like Self
or
	+: Like Self x Integer --> Integer.)


>		   (1+V).image          "6" or "VI" or error (in +)?

This would return "6".  "Like Self" is Integer, so no conversions are needed:
V is a Roman and hence an Integer.

There are, of course, many other ways of doing it.  I feel this one is
most natural for the set of assumptions that I made.

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

matt@bacchus.esa.oz (Matthew Atterbury) (07/13/90)

In article <1990Jul11.184517.16505@swbatl.sbc.com> gilstrap@swbatl.UUCP (Brian Gilstrap - UCI - 5-3929) writes:
>
>I could easily see a language where the default was
>
>	convert the Roman to an Integer no matter if it's first or second
>	(least common denominator)

Why stop at Integer? Assuming the Smalltalk hierarchy, why not convert it to a
Number, then to an Object? Applying this reasoning would lead to the result
of all operations being of type Object ie. a TYPELESS language! Or are you
suggesting full expression analysis to see what the most general class is?

>OR
>	convert the Integer to an Roman no matter if it's first or second
>	(I guess you could call this "specialization of results")

Similarly, why stop at Roman? What if there is a subclass of Roman - then
all Integers and Romans would be converted to it, and if you subclassed that
one later, all your earlier operations would be reconverted. What if Integer
has a number of subclasses - which one? Or, again, are you suggesting full
expression analysis to see what the most specific class is?

>Just because C++ approaches things the way described above doesn't mean it
>has to be that way.
    Smalltalk works like this also.

If you are suggesting expression analysis, what if you get two classes that
are NOT in very close subtrees of the class hierarchy? Do you then say that the
most general form is (probably) Object so the result is converted to Object?
And what is the most specific form?

Basically, I can see how you could argue for your suggestion given the very
simple example of Integer and Roman, but in the context of a class hierarchy,
it seems unworkable. The expression: "1 + 2" says (sort of);
  expression evaluator:
    hello object of type integer of value 1. I have here an object of type
    integer with value 2. will you please perform the '+' operator with it,
    WHATEVER THAT MEANS TO YOU (coz *I* don't know), and tell me the result?
  integer class:
    OK. here's the result. (BTW, it's an object of type integer with value 3).

In smalltalk (and all "real" OO languages IMHO), even integer operations like
'+' are left to the "target" object to perform - they are NOT evaluated by
some global omniscient 'expression evaluator'. If Integer decided that the
result of adding a Roman to itself ought to be a Roman, it could do the
conversion itself - NOTE that this decision is up to Integer, NOT anyone else.

Well, this *was* going to be a short reply. Sorry for rambling on & on & on ...
-- 
-------------------------------------------------------------------------------
Matt Atterbury [matt@bacchus.esa.oz.au]   Expert Solutions Australia, Melbourne
UUCP: ...!uunet!munnari!matt@bacchus.esa.oz.au            "klaatu barada nikto"
ARPA: matt%bacchus.esa.oz.AU@uunet.UU.NET  "life? don't talk to me about life!"

barmar@think.com (Barry Margolin) (07/13/90)

In article <SRA.90Jul10155606@alonzo.ecs.soton.ac.uk> sra@ecs.soton.ac.uk (Stephen Adams) writes:
>If V+V is not a roman (but an integer, 10) then some
>information has been lost.

I don't agree.  + is a function that maps from <integer,integer> to
integer.  When roman inherits this operation, it can generally only deal
with the "integerness" of roman numbers.  The integer class doesn't know
that the roman class exists, so how could integer.+ produce a roman?

Virtual functions can allow base classes to operate at the descendents'
levels in some ways, but it can get pretty difficult to delegate all the
right things.  In your example the + function isn't being overridden by the
derived class, so what virtual functions might it call that would do the
right thing?  And in the V+1 and 1+V cases, how would this generic plus
know whether to call the virtual function of the left or right argument
(I think CLOS multimethods allow the priority of the arguments to be
specified in the generic function definition), or how do you make the order
irrelevant?

Yes, information is lost.  Consider what would happen if the subclass also
has additional data, rather than just overriding a method.  For instance,
suppose there were a flag slot in the roman class that indicated whether it
should be printed in the old style ("IIII" rather than "IV").  How could
integer.+ know how to fill in this instance variable?  I suppose it could
let this default, but you're still losing information, since you'd expect
that two romans with the same style should sum to another with that style.

Actually, that wouldn't be a good way to implement the two styles, but the
object-oriented way also has a similar problem.  In this case, we'd make
another subclass of integer called "old-roman", which also only overrides
the image function and inherits integer.+.  Now, what would you expect
IIII+IV to return?  In fact, it would be reasonable to make old-roman a
subclass of roman, but it would be just as reasonable to go the other way.
Therefore, even if we were to define a mechanism where the result of the
addition were the most recent common ancestor of the two arguments, there's
no inherent reason why IIII+IV should pick one or the other as the result
type.  And what if the most common ancestor were an abstract class; for
instance, suppose there were an intermediate class, roman-without-4, which
would be abstract because it doesn't know how to display 4 (it would have a
pure virtual function image4).

So, in general, using an inherited operation tends to be an
information-losing mechanism.  It's easy to move up the inheritance
hirarchy, but hard to move back down.

--
Barry Margolin, Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar

bertrand@eiffel.UUCP (Bertrand Meyer) (07/14/90)

Just a note on how this is understood in Eiffel, with no
attempt to compare with other object-oriented languages.
(Sorry, it is longer than originally anticipated, since
it starts from the basic ideas.)

	As part of the paradigm, every operation
has one or more arguments, and one of them, the ``target''
of the operation, plays a special role.  This convention
makes it possible to unify the concepts of module and
type into a single one - the concept of class. A class is a module,
packaging together a set of facilities; it is also a type,
describing a set of potential run-time instances. By accepting
that every operation ``applies to'' one distinguished target,
we can merge the two notions by considering
that the facilities packaged in the class (viewed as a module)
are precisely the operations ``applying to'' individual instances
of the class (viewed as a type).

	This is somewhat constraining, sometimes forcing us
to choose ``the'' target of a multi-argument operation in apparently
arbitrary ways, but serves as a powerful structuring facility and
opens the road to many other properties, in particular inheritance.

	The convention is reflected in the standard syntax for
application of operations (features): dot notation, as in

/1/
	document.print (my_macro_package, my_printer)

where the target appears before the period, and any further arguments
appear in parentheses after the operation name.

	Dynamic binding is also predicated on the ``single target''
assumption: if there is more than one version of an operation
applicable to the target, the semantics of object-oriented computation
prescribes that the choice be made on the basis of the run-time form
of the object to which the target is attached. For example, if there
are various types of documents, each described by a different class,
and these classes provide different versions of the feature `print',
then the execution of /1/ will select the appropriate version on the
basis of the run-time type of `document'.

	Not surprisingly, the decision to base dynamic binding on the
type of just one argument (the target) is subject to criticism.
What if multi-criterion discrimination is desired? But this convention
keeps things simple, and the few examples I have seen of mechanisms
attempting to support dynamic binding on more than one argument were
not convincing. 

	Using dot notation for an arithmetic operation might give something
like

/2/
	i.plus (j)

with dynamic binding on the first argument only. The type of the result
of /2/ is the type declared for the result of function `plus',
as defined or redefined in the class corresponding to the dynamic
type of `i'.

	The Eiffel notation for infix routines makes it possible
to use standard syntax instead of /2/:

/3/
	i + j

If i is declared of type C, this assumes that the declaration
of the feature in C uses a name of the form `infix "+"'
rather than `plus':

/4/
	infix "+" (other: C): C is
			-- Sum of current element and `other'
		do
			... Implementation of addition ...
		end -- "+"


This is indeed the kind of declaration found in Kernel Library
classes such as INT, describing integers, FLOAT, describing
floating-point reals, etc.

(As readers familiar with Eiffel will know, we might in some cases
prefer to declare `other' and the result as being of type `like Current'
rather than C.)

	The ``infix'' mechanism (as the similar ``prefix'' mechanism) is,
however, purely syntactical. /3/ is a mere equivalent for /2/,
with the only difference that the feature was declared with name
`infix "+"' rather than `plus'. In both /2/ and /3/, the choice of
the version to be applied depends solely on the type of `i'.
The type of `j' plays no role.

	As with /2/, the type of the result of /3/ is the type declared
for the result of `infix "+"' in the class corresponding to the dynamic
type of the object to which `i' is attached at the time of execution.
If that class is a descendant of C, this result type must itself
be a descendant of C. (A descendant of a class is the class itself,
or a direct or indirect heir.)

-- Bertrand Meyer
bertrand@eiffel.com

bobatk@microsoft.UUCP (Bob ATKINSON) (07/17/90)

In article <1130013@gore.com> jacob@gore.com (Jacob Gore) writes:
>/ comp.object / sra@ecs.soton.ac.uk (Stephen Adams) / Jul 10, 1990 /
>(Of course, if this was a common enough operation, "+" would be better
>defined as
>	+: Like Self x Integer --> Like Self
>or
>	+: Like Self x Integer --> Integer.)

I have run across a similar desire in C++ for a "same type as receiver" 
type expression in constructing collection classes.  For example, I
would like to express the fact that all collections can return
a copy of themselves.  Naturally, I would like to put this declaration
in the same class in which I express the other fundamental collection
operations, such as, for example, insertion of elements or enumeration.
This is in class Collection.  Thus, I am tempted to write:

	class Collection 
	{
	public:
		virtual	Collection* 	Copy();
	};

but of course, this gives the incorrect return type, since expressions
of the form 

	class Set : public Collection { /* ... */ };
	//...
	Set* pset = pSomeOtherSet->Copy();

give a type error.


What I really want to say is something like

	class Collection
	{
	public:
		virtual	typeof(*this)*	Copy();
	};

Though I have only thought about it in briefly, I believe this interacts
cleanly with the automatic type conversions of Derived* to Base*.

It strikes me that the typeof() operator might be generally useful
in other situations as well.

One might ask if parameterized types would allow us to express this
construction.  I don't see how the current parameterized type proposal
would accomplish it.


	Bob Atkinson
	Microsoft

sakkinen@tukki.jyu.fi (Markku Sakkinen) (07/26/90)

In article <SRA.90Jul10155606@alonzo.ecs.soton.ac.uk> sra@ecs.soton.ac.uk (Stephen Adams) writes:
>This might be a naive question, but it has been puzzling me
>for a while.  Consider the class integer.  This class
>supports two methods:
>
>	+       which takes another integer and returns the sum
>	image   which returns a string denoting the integer
>		as a decimal number
>
>so 2+4 -> 6  and  (6).image -> "6"
>
>Suppose I create a subclass called roman.  I provide a new
>method called image which returns roman numerals e.g.
>roman_6.image -> "VI".  The + operation is inherited from
>integers.

This is a very good question, in fact.
The great majority of OO languages treats basic types such as integer
differently from ordinary classes.
There are two main approaches:
1. Simula and many others (including C++ and Eiffel) don't even pretend
   that 'integer' is a class; thus you cannot create any subclasses
   from it.
2. Smalltalk and some other languages pretend that 'integer' is a class
   just like any other. However, it is almost impossible to define
   subclasses that make any sense (I'll qualify this below).

The problem at hand means that it is only desired to override
one superclass method in the subclass, thus instances of the subclass
should be completely similar to instances of the superclass.
If OOP is as great for code reuse as advertised, then clearly
one should not be required to write anything but the overriding
method in the subclass definition.

In Smalltalk-80, Integer happens to be an abstract class,
so in Smalltalk we must take some concrete subclass (e.g. SmallInteger)
to stand for 'integer' above. (The problem is different for an abstract
superclass: '+' may not be implemented there at all, and indeed it isn't
in Smalltalk's Integer class.)

The instances of such Smalltalk classes as SmallInteger are immutable,
and they are conceptually never created; all occurences of e.g. '5'
can be regarded to refer to the same immutable object.
It would seem to me that if one defines Roman as a subclass of SmallInteger,
there is no way to create an instance of Roman with a given value.

It appears that for the problem to be solvable,
'integer' must be a user-defined class both in Simula-like
and Smalltalk-like languages. Let's call it 'MyInteger' to make this clear.
It will obviously need exactly one instance variable,
which is an ordinary integer.
It will also need an initialisation method (constructor in C++).
The '+' method must evidently create a new instance of MyInteger
for its return value, just like 'image' must create a new string instance.

>I declare the following variable and initialize it
>
>	V:roman := make a roman with the value 5
>
>I have two questions:
>
>(a)  what should   (V+V).image  yield?  "10" or "X"?
>		   (V+1).image          "6" or "VI"?
>		   (1+V).image          "6" or "VI" or error (in +)?
>
>(b)  what do various object oriented systems actually do?
>
>
>If V+V is not a roman (but an integer, 10) then some
>information has been lost.  This might be reasonable but it
>does seem surprising.

(b) Because we need the user-defined class MyInteger, _both_
'V+1' and '1+V' will in most languages cause an error (at compile
or run time depending on whether the language is statically typed or not).
The integer '1' must therefore first be converted into a MyInteger.
However, at least C++ will do automatic conversions if appropriate
conversion operators have been defined.

(a) Since you do not override '+' but inherit the superclass method,
which returns a MyInteger, you will get the first alternative in all cases.
If you do redefine '+' in Roman to yield a Roman result (with a MyInteger
second operand) you will get the second alternative except in the last case.
(In C++ however, if '+' is _virtual_ in MyInteger, you are not allowed to
change the result class in the overriding method.)

In C++ you can actually define distinct methods for each of the three
above operand type pairs and give each of them exactly the result type
that you like, independently. The appropriate method will then be selected
at compile time; so if you use pointers to objects, the actual run-time
class of the objects pointed to has no effect. - In CLOS, the "multi-method"
principle means that you can also define different methods for different type
pairs, but the selection will happen at run time based on the actual classes
of the objects.
By suitably defining several 

>Stephen Adams                        S.Adams@uk.ac.soton.ecs (JANET)
>Computer Science                     S.Adams@ecs.soton.ac.uk (Bitnet)
>Southampton S09 5NH, UK              S.Adams@sot-ecs.uucp    (uucp)

It really wasn't too naive at all!

Markku Sakkinen
Department of Computer Science
University of Jyvaskyla (a's with umlauts)
Seminaarinkatu 15
SF-40100 Jyvaskyla (umlauts again)
Finland
          SAKKINEN@FINJYU.bitnet (alternative network address)

bertrand@eiffel.UUCP (Bertrand Meyer) (07/26/90)

From <1990Jul26.120125.7955@tukki.jyu.fi> by sakkinen@tukki.jyu.fi
(Markku Sakkinen):

> Simula and many others (including C++ and Eiffel) don't even pretend
>    that 'integer' is a class.

	This is not quite correct with respect to Eiffel. INTEGER is an
``expanded type'' based on class INT, itself declared in the Kernel
Library as

class INT export

	infix "+", infix "-", ...
	prefix "+", prefix "-",
	infix "<", infix "<=", ...

inherit

	COMPARABLE;

	FLOAT_CONV

feature

	infix "+" (other: INT): INT is ...

	etc.

end -- class INT


Type INTEGER itself is an abbreviation for ``expanded INT'',
meaning that instances of INTEGER do not need to be allocated
dynamically, the way instances of a class type such as INT do.

This was not described in the book ``Object-Oriented Software
Construction'', which still treated integers and three other
basic types in a special way, as implied by Mr. Sakkinen.
However the full type system of Eiffel, with the above characteristics,
is described in the current reference on the language,
``Eiffel: The Language'' (August 1989) and in an earlier document
entitled ``Eiffel Types'' of which a quasi-final version was posted
in comp.lang.eiffel on April 2, 1989 (message id <133@eiffel.UUCP>).

The language definition treats class INT and its colleagues
(CHAR, FLOAT, BOOL, DFLOAT) as normal classes, which may have
descendants. In Interactive's current Eiffel implementation, there
are still some limitations on writing classes that inherit from
INT and the like; these are corrected, however, in the forthcoming
release of our compiler. 

All this was done with the explicit aim of making the Eiffel type
system completely consistent, and uniformly based on the notion of
class.

-- Bertrand Meyer
bertrand@eiffel.com

jimad@microsoft.UUCP (Jim ADCOCK) (07/28/90)

In article <1990Jul26.120125.7955@tukki.jyu.fi> sakkinen@jytko.jyu.fi (Markku Sakkinen) writes:
>In article <SRA.90Jul10155606@alonzo.ecs.soton.ac.uk> sra@ecs.soton.ac.uk (Stephen Adams) writes:
>1. Simula and many others (including C++ and Eiffel) don't even pretend
>   that 'integer' is a class; thus you cannot create any subclasses
>   from it.

Yes, except with about a page of code C++ allows you to turn the 
built-in primitive 'int' [or other primitive] into a a true class 'Int'
than acts 'identical' to an int, but now is a full-class class, and can
be subclassed.  See Hanson "The C++ Answer Book" for the 'right' way to
do this.

>In C++ you can actually define distinct methods for each of the three
>above operand type pairs and give each of them exactly the result type
>that you like, independently. The appropriate method will then be selected
>at compile time; so if you use pointers to objects, the actual run-time
>class of the objects pointed to has no effect. 

Yes and no -- it depends on how you program it.  Using the matrix/vector
example, one might want to define something like:

matrix& operator*(matrix& m, vector& v);
matrix& operator*(matrix& m, matrix& v);
....

where matrix and vector are really appropriate protocols.  Then one could
define various concrete classes answering these protocols:  packed_matrix,
sparse_matrix, packed_vector, packed_matrix.... and the overloaded operators
make sure that the right protocols get selected when something like:

	a_sparse_matrix_ref * a_packed_vector_ref;

is written.  In general, then one has to explicitly program to get a virtual
virtual function dispatch off both the left hand side and the right hand side
in order to get to the actual do-the-work function, say:

matrix& MpySparseMatByPackedVector(sparse_matrix& m, packed_vector& v).

But, the nice thing is that the protocols are type-checked at compile time
so that things like:

	a_sparse_matrix_ref * a_banana_ref;

are caught at compile time [ assuming bananas aren't part of the math
system you want to define :-]

The real problem is that unless one is clever, one ends up implementing
O(n^2) actual add or multiply routines that do the hard work.  "Clever" in
this particular example *might* mean making operator[] virtual, so that
multiply and add routines can be written that don't care about the sparse-ness
or packed-ness of a matrix or vector.  This might reduce the problem such
that there are only four flavors of each of add, sub, mpy, ....