[comp.lang.eiffel] Preconditions and Multiple Inheritance

db@lfcs.ed.ac.uk (Dave Berry) (03/09/90)

I've been re-reading an exchange about preconditions and multiple inheritance
that took place in comp.lang.eiffel last year.  The example under
consideration was a class COMPANY_PLANE that inherited from PLANE and 
ASSET.  The precondition for "fly" in PLANE was that the user should
be a qualified pilot, the precondition for "use" in ASSET was that
the user should be a company employee, and the resulting precondition
for "fly" in COMPANY_PLANE was that the user should be both a qualified
pilot and a company employee.  The problem with this formulation is
that it strengthens the precondition of "fly" in the subclass instead
of weakening it.

The response to this was that a well-designed PLANE class would allow for
more specific qualifications, such as being allowed to fly the plane
by its owner.  Similarly, the ASSET class should allow for other
qualifications than being an employee, because not all employees can use
every asset.  So either:

	1. The class hierarchy should have been better designed in the
	   first place.
  or
	2. You should go back and redesign the class hierarchy.

It seems to me that case 2 is against the spirit of other decisions
made in the design of Eiffel.

I say this because the two most controversial parts of the Eiffel type
system, namely the covariant rule for types of the arguments of redefined
functions and the orthogonality of inheritance and visibility, were
expressly designed to handle cases when the inheritance hierarchy could not
be redesigned.  They are a recognition of the practical truth that we don't
always have an ideal base to work from.

By analogy, the "require" clause should make the same allowances.  There
may be times when we can't guarantee that the precondition of a redefined
function will be weaker than that of the original.  Eiffel shouldn't
enforce contravariance for assertions if it doesn't enforce it for
types.


I can think of two possible approaches:

	1. Make "require or" the normal way to specify preconditions
	   of redefined routines, as at present, but to keep "require"
	   as an option (or allow "require and" instead).
	
	2. Insist on covariant assertions, by replacing "require or"
	   with "require and".

Presumably the restrictions that currently apply to redefined routines with
argument types that are subtypes of the original would also apply to a
redefined routine that used "require and".

I admit that this seems an extreme point of view, but the covariance rule
seemed strong when I first met it.  Certainly the current situation, where
one sort of precondition (argument types) is covariant and the other
("require" clause) is contravariant, seems inconsistent.

Comments?

 Dave Berry, LFCS, Edinburgh Uni.      db%lfcs.ed.ac.uk@nsfnet-relay.ac.uk

"The thought of someone sharing one's own preferences without also sharing
 one's aversions strikes most people as utterly inconceivable." - C. A. Tripp.

db@lfcs.ed.ac.uk (Dave Berry) (03/21/90)

I'm posting this for Andreas Schramm
(schramm%uucp.gmdtub@de.uni-dortmund.informatik.unido)
because posting to Usenet is not implemented at his site.

Dave Berry.

-----------------------------------------------------------------------------

>                                ...  Certainly the current situation, where
> one sort of precondition (argument types) is covariant and the other
> ("require" clause) is contravariant, seems inconsistent.
> 
> Comments?

According to Dr. Meyer, covariant redefinition of
routine argument types is an indispensable feature.
However, if routine argument types are seen as preconditions,
their covariant redefinition violates the
"programming as contracting" paradigm,
which requires that preconditions must not be strengthened (contravariance).

In order to reconcile the two conflicting requirements I propose:

	Make the extent to which argument type redefinitions
	in descendant classes are permitted part of the contract.

	Enforce that routine redefinitions are
	bound by the contract as it has been defined
	by the original top-most supplier of a routine.
 
This appears tolerable since the original supplier should
usually know in advance which argument type restrictions will make sense
when subtypes are introduced. 
This assumption is justified because the original definition
is supposed to fix the meaning of a routine and all its redefinitions,
while redefinitions are supposed to be
just specialized implementations [OOSC 10.1.10].

Note that it has never been a requirement that
contracts are phrased in statically checkable terms.
In fact, preconditions (which are part of a contract) often are not.
Thus an appropriately phrased contract may e.g.
tie routine argument types to the state space of the object involved,
which amounts to covariant argument type redefinitions.

Some time ago I wrote a letter to Dr. Meyer which contained
a suggestion how to to embed this idea in Eiffel.
The suggestion was:

	The top-most definition of a routine fixes its
	argument types _textually_ once and for all,
	i.e. for redefinitions the argument types
	have to be copied textually.

The necessary flexibility is preserved
because an argument type need not necessarily be a class type
but may be of the form "like a"
or may refer to a formal generic type parameter.
This, by the way, requires the following modification of the rule above:

	Anchor renamings and instantiations of the parents' formal generic
	type parameters at inheritance time have to be taken into account.

An argument declaration "x: A" would mean that the contract includes
"Every redefinition shall accept an argument of type A".

An argument declaration "x: like a" would mean that the contract includes
"Every redefinition shall accept an argument of a dynamic type
that conforms to the declared type of feature a
in the class of the type of the object involved at run-time."

The former phrase is static, the latter dynamic in nature.

In order to imitate the original flexibility of argument type redefinitions,
at worst an artificial anchor might have to be introduced;
but it strikes me that covariant argument type redefinitions
often refer to an already existing feature anyway.

Andreas Schramm

 Dave Berry, LFCS, Edinburgh Uni.      db%lfcs.ed.ac.uk@nsfnet-relay.ac.uk

   "Excuse me, Mr. Policeman, but someone seems to have stolen my soul.
    What're you going to do about that then, Officer?" -- Here and Now.