paj@uk.co.gec-mrc (Paul Johnson) (03/11/91)
Another thought to add to the co/contra variance debate. Why not have both? Page 80 of `Eiffel: The Language' (version 2.2) states that: A signature S = ( <X1, X2, X3, ... >, <R> ) conforms to S' = ( <X1', X2', X3', ... ), <R'> ) if and only if: 1: The two sequences have the same number of elements. 2: Every type in each of the two sequence components of S conforms to the corresponding type in the corresponding component of S'. Note for the uninitiated: --------------------------------------------------------------------- The term `signature' refers to the types taken and returned by a procedure or function. The Xn terms are the argument types and the R terms are the return types (if any). Part 2 (in conjunction with one or two other rules) states that a feature in a descendant class can only take and return types which are descendants of the types taken and returned by the same function in the ancestor where it was defined. This is known as covariance, and is needed in Eiffel for the definition of functions which take arguments of type `like f' where `f' is some feature which may be redefined in subsequent types. It also leads to type errors when polymorphism is applied to the class which contains the function. Suppose we have a class ANCESTOR which defines a function `foo( a: ANCESTOR )'. We then derive a class DESCENDANT which inherits from ANCESTOR and redefines `foo' to be `foo( a: DESCENDANT )'. We then create an instance of DESCENDANT and set a reference of type ANCESTOR to this new instance. It is now possible to call the new version of `foo' (which expects something of type `DESCENDANT' with an argument of type `ANCESTOR'). The contrary scheme (contravariance) would have signatures conforming if the types in the X sequence of S' conformed to the types in the X sequence of S. This is the other way around and causes great problems. Interestingly, the Software Contracting Principle applied to assertions and requirements in descendant classes insists on a contravariant scheme, but that is not what we are discussing here. --------------------------------------------------------------------- End of note. Start of suggestion. Why not remove the restrictions on the argument types. Contravariant typing may not be very useful, but there is no reason to disallow it since it is always type safe. If the type checker can catch misuses of covariant typing then the compiler can allow both. Paul. PS for Dr. Meyer. This is not the Sibling-Supertype rule I talked about at TOOLS '91. Two papers describing that are on their way to you. Paul Johnson UUCP: <world>!mcvax!ukc!gec-mrc!paj --------------------------------!-------------------------|------------------- GEC-Marconi Research is not | Telex: 995016 GECRES G | Tel: +44 245 73331 responsible for my opinions. | Inet: paj@gec-mrc.co.uk | Fax: +44 245 75244
rick@tetrauk.UUCP (Rick Jones) (03/13/91)
In article <860@puck.mrcu> paj@uk.co.gec-mrc (Paul Johnson) writes: > >Another thought to add to the co/contra variance debate. Why not have >both? > > [detailed definition of the co/contra variant principles] > >Why not remove the restrictions on the argument types. Contravariant > ^^^^^^^^^^^^^^^^^^^^^^^ >typing may not be very useful, but there is no reason to disallow it >since it is always type safe. If the type checker can catch misuses >of covariant typing then the compiler can allow both. I'm not sure if you meant to say that, but you can't actually _remove_ the restrictions. What you can do is allow arguments to be redefined as either ancestors or descendants of the inherited definitions. I.e given a portion of an inheritance tree: A / \ / \ B D / / C then a function "foo (arg: B)" in some class may be redefined in a descendant as either "foo (arg: C)" (covariance), or "foo (arg: A)" (contravariance). It _cannot_ be redefined as "foo (arg: D)". I would agree in principle that since contravariance is guaranteed safe, there seems no reason to disallow it because covariance is supported. However, this leads to a logical anomaly. To take an extreme case, consider the following: class X declares: foo (arg: STRING) | | class Y redefines: foo (arg: ANY) -- legal by contravariance | | class Z redefines: foo (arg: TREE) -- legal by covariance If class Y does nothing else apart from redefining foo, then you could arguably say that class Z should be able to inherit directly from class X. This _would_ require removal of all type checks on arguments, which seems from a practical point of view to be a nonsense. It does suggest that allowing co- and contra- variance to coexist would lead in practice to more confusion than it resolved. -- Rick Jones, Tetra Ltd. Maidenhead, Berks, UK rick@tetrauk.uucp The dynamics of a freeway approximates to a shoal of fish in 1.5 dimensions