Type safe Eiffel
The following is a short description of a solution to the catcall problem (recently posted in comp.lang.eiffel as well):
In Eiffel it is easy to generate runtime type errors. Therefore Eiffel cannot (yet) be considered to be a typesafe language. A modern Eiffel shall be typesafe.
A runtime type error (called catcall in Eiffel speak) can happen because Eiffel has
a) Polymorphic attach
b) Covariant redefinition or routine arguments
c) Promiscuous generic conformance
Polymorphic attach (a) is at the heart of object orientation in all OO languages. The textbook example is a graphic library with an abstract base class SHAPE and some concrete descendants like RECTANGLE, CIRCLE, ELLIPSE, TRIANGLE, POLYGON, etc.
Eiffel is the only language (at least I don't know another language) which allows covariant redefinitons of arguments (b) usually in the form of anchored declarations like
deferred class COMPARABLE feature is_less alias "<" (o:like Current) ... end.
which adds a lot of reuse possibitlities and expressiveness.
However with (a) and (b) lead to type errors like
a,b: COMPARABLE ... a := "Hello" -- possible because STRING conforms to COMPARABLE b := 1 -- possible because INTEGER conforms to COMAPARABLE
if a < b -- runtime type error!! then ... end
With promiscuous generic conformance (c) another class of type errors are possible, e.g.
s: ARRAYED_SEQUENCE[COMPARABLE] s_int: ARRAYED_SEQUENCE[INTEGER] i: INTEGER ... create s_int s := s_int ... s.extend_rear(1) s.extend_rear("Hello") ... i := s_int + s_int -- runtime type error
This problem is shared by other languages like C++ and Java which allow promiscuous generic conformance as well.
A solution has been sought since the beginning of Eiffel. Bertrand already stated the catcall problem (combination of (a) and (b)) in his book object oriented software construction very clearly. Many solutions have already been proposed but none of these has been satisfactory.
But the solution is very simple. Bertrand has already given the key idea of the solution in his OOSC book. Polymorphic attach and covariant redefinitions of arguments in combination can lead to type errors. Neither polymorphic attach nor covariant redefinition alone is problematic, only the combination.
The only problem to solve: Disallow the combination of polymorphic attach and covariant redefinition in combination. Since both come with inheritance we need 2 forms of inheritance. One that allows polymorphic attach of an object to its parent class but disallows covariant redefintion of arguments and one form of inheritance which disallows polymorphic attach and allows covariant redefinitions. The former can be expressed with "inherit ->" and the latter with "inherit" (just a proposal, another syntax is possible as well). E.g.
class class RECTANGLE INTEGER inherit -> inherit SHAPE COMPARABLE ... ... end end
With that distinction everything is fine for (a) and (b). The solution is so simple and clear. But it has some consequences which some might not like.
1. Universal conformance is no longer possible
2. Constrained genericity has to be adapted
The definition of the class ANY leads to implicit covariant redefinition of arguments of all its descendants due to the signature
is_equal (other: like Current): BOOLEAN
Therefore no class can "inherit ->" from ANY. But is this a problem? I mean a real problem? Is it really necessary that we have a type to which all objects can be attached and is that type necessarily ANY? Most will answer "yes" to all that questions. This is the main obstacle for a modern type safe Eiffel.
But the answer is "no". It is not a real problem. All things which can be achieved by universal conformance to ANY can be achieved by other means.
The second impact on constrained genericity can be resolved be expressing the constraints in the same two manners as inheritance, e.g.
class CG1[G -> CONFORMANT_CONSTRAINT] ... end
class CG2[G: like COVARIANT_CONSTRAINT] ... end
(or the difference expressed the by another syntactic construct).
If the user wants to express a constraint he usually means the second form. There is rarely the need that formal generic must be attached to its constraint. The main reasons for the constraint is to express which features can be called on formal generics.
The last form (c) of runtime type errors can be solved by forbidding generic conformance (or using a more disciplined one like e.g. scala has).
There is one strong argument against type safe Eiffel:
It is not backward compatible!!!
But: A type safe Eiffel which is backward compatible does not seem to possible.
Main reason: ISE Eiffel can generate runtime type errors, type safe Eiffel makes runtime type errors impossible. Therefore a compiler of type safe Eiffel cannot compile programs written in ISE Eiffel.
For details see also the white paper at http://tecomp.sourceforge.net -> white papers -> catcall solution