[comp.lang.eiffel] Deferred routines and name clashes

florman@randvax.UUCP (Bruce Florman) (04/25/89)

    I recently came across a problem with multiple inheritance and 
deferred routines which, while not insurmountable, can only be handled 
by some ugly, inefficient, and possibly non-portable programming 
practices.  

    I am writing a small discrete-event simulation, and one of the 
most primitive classes is the LOCATABLE class, which is used to 
represent physical objects whose location may be expressed as a 
single point.  One of the exported routines of LOCATABLE is a 
deferred routine named `location' which returns an object of type 
POINT.  There are three heirs to LOCATABLE which provide alternative 
implementations of the location routine.  The first is the INDEPENDENT_
LOCATABLE class, which simply stores the location in an attribute named 
`current_location'.  The second is the DEPENDENT_LOCATABLE class, which 
defines an attribute named `host' of type LOCATABLE, from which it gets 
its location.  This allows me to represent things which are discrete 
objects, but which are attached to other locatable objects, generally 
ones which can move.  The last heir to provide an effective definition 
of the location routine is the SEMI_DEPENDENT_LOCATABLE class, which 
inherits from both INDEPENDENT_LOCATABLE and DEPENDENT_LOCATABLE.  Its 
definition returns its current_location attribute if its host is void, 
and returns its host's location otherwise.  This allows the represen-
tation of objects which are only sometimes attached to other objects.

    Another class which inherits from LOCATABLE is the class called 
TWO_WAY_RADIO, which is used to represent things which both transmit 
and receive radio signals.  A TWO_WAY_RADIO must be LOCATABLE so that 
the simulation can determine such things as whether any two of them 
are close enough together to receive each other's signals, but the 
actual implementation of the location routine is of no concern to the 
TWO_WAY_RADIO class.  Since there exist large radio installations whose 
location is pretty well fixed (INDEPENDENT_LOCATABLE), and radios which 
are bolted into cars and boats and planes (DEPENDENT_LOCATABLE), and 
portable radios which may be passed from one person to another (SEMI_
DEPENDENT_LOCATABLE), we don't want to tie TWO_WAY_RADIO directly to 
any of those classes.  

    The problem arises when I try to define an effective class which 
inherits from TWO_WAY_RADIO.  When I try to define, for example, 
SQUAD_CAR_RADIO -- the radio installed in a police car -- simply as 
an heir to both TWO_WAY_RADIO and DEPENDENT_LOCATABLE, the compiler 
complains about a name clash, despite the fact that the name coming 
from one parent has no effective definition, and the name coming from 
the other parent is simply a definition of the very same deferred 
routine.  

    The work around for this problem is simple, but stupid.  The class 
is defined as shown below.

	class SQUAD_CAR_RADIO
	    ...
	inherit
	    DEPENDENT_LOCATABLE;
	    TWO_WAY_RADIO rename location as radio_location
	feature
	    ...
	    radio_location: POINT is do Result := location end;
	    ...
	end -- class SQUAD_CAR_RADIO

It certainly seems pointless and inefficient that every time any of 
the TWO_WAY_RADIO routines accesses the location, it will have to go 
through this glue routine.  It seems especially pointless that, given 
the above definition, any routines which are effectively defined 
by classes above TWO_WAY_RADIO in the inheritance graph will also 
have to go through this glue routine whenever they access the 
location.  This is because, as I pointed out in another posting a 
couple of weeks ago, when an entity's dynamic class has more than 
one effective definition for a given routine, the definition from 
the last parent in the inheritance clause takes precedence, regardless 
of whether it has been renamed.  However, this inefficiency can be 
eliminated by simply reversing the order of DEPENDENT_LOCATABLE and 
TWO_WAY_RADIO in the inheritance clause above.  I still haven't 
heard from ISE whether this should be considered an implementation 
dependent feature of Eiffel (maybe when people get back from Paris).  

    In _Object-oriented_Software_Construction_, Dr Meyer emphasizes 
the concept of deferred classes as partial implementations of abstract 
data types.  It would seem to be consistent with this idea that two 
deferred classes which each implement half of the abstract type may 
be combined in a straight forward manner to fully implement that data 
type.  I feel that a more perfect Eiffel compiler would recognize the 
case where a name clash arises from one parent effectively defining 
an inherited routine that another parent doesn't.  It would accept 
the inherited definition as the effective definition for both parents, 
possibly issuing a warning similar to the one issued for repeated 
inheritance.  

    Undoubtedly there will be cases where the above behavior is not 
desirable, so probably there should be a method that allows the 
programmer to prevent the compiler from merging the definitions.  
Perhaps this would be done by explicitly resolving the clash through 
renaming.  

    Aside from its exorbitant cost and the fact that it isn't 
available for the Macintosh environment, I'm still a great fan of 
Eiffel.  However it does still have some weaknesses, and it would be 
nice to see them addressed in future releases.

-- Bruce Florman
   florman@rand.org

bertrand@eiffel.UUCP (Bertrand Meyer) (05/01/89)

    In <1981@randvax.UUCP>, florman@randvax.UUCP (Bruce Florman)
accurately describes a not infrequent type of situation for which the
Eiffel 2.1 notation is too complicated. The problem arises in multiple
inheritance when the programmer wishes to implement without much ado
a deferred routine from one parent by a compatible effective routine
inherited from another.

    The following is a compressed version of his description, abstracting
from his particular example.  (An asterisk, applied to either a class
or a routine, means ``deferred''.  A plus means ``effective'',
i.e. non-deferred.)

        ________          ________
        |      |          |      |
        | A*   | f*       | B+   | f+
        |      |          |      |
        --------          --------
           ^                  ^
           |                  |
           |                  |
           \                 /
            \               /
             \             /
              \           /
               \         /
                \       /
                 \     /
                  \   /
                   \ /
                ________   
                |      |   
                | C+   | f+
                |      |  
                --------

    Let class A have a deferred routine f and class B have an effective
routine, also called f, with compatible signature and specification.

    [Actually, if the classes have been designed separately, there is
    no reason why the routines should have the same name. I'll keep
    this assumption for the time being for consistency with Mr. Florman's
    example; it will be removed later.]

>     [Mr. Florman tries] to define an effective class C which 
> inherits from both A and B, where f from B provides the implementation
> for the deferred f inherited from A.  But the compiler 
> complains about a name clash, even though the name coming 
> from one parent has no effective definition, and the name coming from 
> the other parent is simply a definition of the very same deferred 
> routine.  
> 
>     The workaround for this problem is simple, but stupid.  The class 
> is defined as shown below.
> 
>     class C
>         ...
>     inherit
>         A;
>         B rename f as effective_f
>     feature
>         ...
>         f (...) is do effective_f (...) end;
>         ...
>     end -- class C
> 
> It certainly seems pointless and inefficient that every time any of 
> the C routines accesses the location, it will have to go through this
> glue routine. [...] I still haven't 
> heard from ISE whether this should be considered an implementation 
> dependent feature of Eiffel (maybe when people get back from Paris).  

[It is strongly recommended that readers refer to the original posting for
a more complete discussion.]

    Well, ``people'' have returned from Paris (at least some of the people
have) and are slowly recovering from jet lag. The problem was indeed
discussed at the Eiffel User conference there and the 2.2 solution
described. (One user contribution which discussed the problem was
the paper by Philippe Drix of ESEO in Angers.) Note that the
``simple but stupid workaround'' described by Mr. Florman may be found in
several places in the 2.1 graphics library (which fortunately will soon
be history).

    A solution is provided in Eiffel 2.2 by the ``join'' subclause of the
``inherit'' clause. This is applicable in multiple inheritance when the
programmer wishes to use an inherited effective routine as implementation
for one or more inherited deferred routines, without using the ``stupid
workaround'' necessary in previous releases.

    The solution is the following:

    - Make sure that, after renaming, the effective routine and all the
    deferred routines it is meant to implement have the same name.
    [In Mr. Florman's description, this was the case without renaming
    since both routines had the same name, f.]

    - This causes a name clash.

    - The name clash is made legal by introducing a subclause of the form
    ``join f'' for all deferred routines that are to be implemented
    by the effective routine. (In the above example and most practical
    cases there is only one deferred f, but the mechanism is applicable
    to any number.)

In the above example the solution becomes:
 
     class C
         ...
     inherit
         A
            join f;
         B
     feature
         ...
         ... no special declaration is necessary ...
         ...
    end -- class C


    Consider now the more general case in which the original
deferred and effective routines did not necessarily have the same name:

        ________          ________
        |      |          |      |
        | A*   | f*       | B+   | g+
        |      |          |      |
        --------          --------
           ^                  ^
           |                  |
           |                  |
           \                 /
            \               /
             \             /
              \           /
               \         /
                \       /
                 \     /
                  \   /
                   \ /
                ________   
                |      |   
                | C+   | f+
                |      |  
                --------

    Then the declaration of C should be:
 
     class C
         ...
     inherit
         A
            join f;
         B
            rename g as f
     feature
         ...
         ... no special declaration is necessary ...
         ...
    end -- class C


    Of course, f could have be renamed rather than g, with an inheritance
clause of the form

    inherit
        A
            rename f as g
            join g;
        B
    feature
        ...

    Syntactically, the ``join'' clause comes after ``rename'' and
``redefine'' if any. It is of the form

    join f1, f2, ...

where f1, f2, ... are deferred routines in the corresponding parent class.
For each routine fi listed in this subclause, the class must contain
exactly one effective routine with the same name and a compatible
signature/specification. (Otherwise the class is erroneous.)
Then the meaning of the subclause is simple: ``Implement each fi in this
class by the corresponding effective routine.''

To take the ``join'' mechanism into account, the renaming rule
(see ``Object-Oriented Software Construction'', page 278, second box)
should be updated as follows:

    |-----------------------------------------------------------------|
    | Renaming rule: A name clash is said to occur in a class D when  |
    | two features inherited from respective parents B and C have the |
    | same name f in D (taking into account any renaming done in D).  |
    |                                                                 |
    | Such a name clash is only permitted in the following two cases: |
    |                                                                 |
    | 1. (Sharing in repeated inheritance.) If f was defined, etc.    |
    |    [Text unchanged until ``(if it is a routine)''.]             |
    |                                                                 |
    | 2. (Joining deferred routines.) If the routine corresponding to |
    |    f in C is deferred, the routine corresponding to f in D is   |
    |    effective, they have compatible specifications, and          |
    |    the ``inherit C'' clause in B contains a ``join'' subclause  |
    |    listing f.                                                   |
    |                                                                 |
    | Any other name clash is illegal (and should be removed by       |
    | renaming).                                                      |
    |-----------------------------------------------------------------|


    A more complete description of the mechanism is contained in the
document ``Release 2.2 overview'', which will be posted later. Please
note that fine details of the join subclause (including the keyword
itself) are still subject to change.

-- Bertrand Meyer
bertrand@eiffel.com