[comp.lang.eiffel] The ``obsolete'' facility in Eiffel 2.2

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

    Perfect reusable components are not obtained at the first shot.
Yet if one is aiming at a full-fledged industry of reusable software
components (as the goal is with Eiffel), perfection is what we should
strive for. (Eventually, that is.)

    This raises the question of what you do when you have produced a first
version of a reusable class, or even a second and a third, and you realize
that you could have done better. Two spirits are at odds:

    - The GROP (Great Temptator Of Perfectionism), who says:
    ``Correct it here and now before it is too late!''

    - The GAIB (Guardian Angel of the Installed Base), who says:
    ``Think of the current users!''

    Both have advice worth listening to and it would be nice to be able to
placate them both.

    In Eiffel 2.2 we have devised what I hope will be a long-term
solution by recognizing that, careful as they may be, developers
of Eiffel libraries (including us, the developers of the basic libraries)
will always run into cases in which, after releasing a library class,
they suddenly experience what in French is called ``esprit de l'escalier''
or ``wit of the staircase'': a great thought which unfortunately is
an afterthought, like a clever reply that would have stunned all the
other dinner guests - if only you had thought of it before walking down
the stairs after the party is over.

    If the afterthought is the realization that a significant overhaul of
the class would make it considerably better, what you should do is go
ahead and rewrite the class. If you want to appease the
GAIB, you can leave the old version around but cease to document it in the
official documentation. This is what we did in version 2.1 (July 1988)
for binary search trees. Here the initial design was not ``wrong'',
but the introduction of constrained genericity into the language made it
possible to have a much nicer version of the class. For compatibility
we left the old class under a different name. The new version
(BIN_SRCHTREE) is the only one that is documented in the manuals.

    Of course, if you uncover a serious design mistake in the original
version you shouldn't leave it around. In this case the GROP wins handily.

    A more subtle case is one in which there is nothing fundamentally
wrong with the functions offered but you realize the interface to these
functions (in terms of exported routines and their signatures) could be
improved. This includes three important example situations:

1.  - You can think of a better name for a routine.

2.  - You want to advise programmers not to use the routine any more.

3.  - You can think of a better signature.

    Situation 1 may occur as you are doing an after-the-fact cleanup of
your library and realize that naming conventions could be made more
consistent.

    Situation 2 may arise when the routine's action is not needed any more
(as with a routine which performed some initialization which you later
realize can be carried out automatically on object creation).

    Situation 3 may occur (among other cases) when you realize that a
routine has too many arguments and should be split into two or more
routines. For example, you may have a procedure adding a subwindow to a
window, under the form

    w.add_subwindow (other_window, horizontal_position, vertical_position) 

but then you realize that it would have been better to omit the last two
arguments and have the subwindow be initially positioned at the top
left corner of the parent window, and let clients move it if needed by
using a specific ``move'' procedure, which is needed anyway.

    (This is a hypothetical example, although I believe it corresponds
    to guidelines that are usually good. In general, a routine should
    only include among its arguments what may be called
    ``indispensable arguments'', as opposed to ``options'';
    an option is recognized by the class's ability to set reasonable
    default values, as for horizontal_position and vertical_position
    in this example - whereas there is no reasonable default for
    other_window, which is thus an indispensable argument. According to
    these guidelines, options should be set separately,
    using specific procedures such as ``move''.
    Again these are only guidelines; exceptions may be justified.
    Some of these issues were discussed in an old and otherwise rather
    obsolete article of mine, ``Principles of Package Design'', 
    Communications of the ACM, July 1982.) 

    Cases 1 and 3 above often involve changes small enough that it is
tempting to heed the GAIB's advice and resist any change at all. But in
the long term this is dangerous. Here the GAIB is really a front-man for
the hideous DECHOP (Devil of Eternal Compatibility with the Horrors Of the
Past) which, among its many claims to infamy, gave us such programming
languages as Fortran 8X.

    What we want is a way to gently phase out old features without
punishing early users.

    This is what has been attempted with the ``obsolete'' facility in
Eiffel 2.2. The simplicity of this facility belies the preceding rather
lengthy prologue. Syntactically, it is a notation that applies to
routines. The syntax of a routine normally begins with

    routine_name (arguments)  is

        ... routine body ...

A routine may now be declared ``obsolete'' by adding a clause of the form

    obsolete "Message"

just before the ``is''. ``Message'' is any string. [Its presence is
optional.] Here is an example taken from the new ARRAY class in
the Basic Library:

    entry (i: INTEGER): T obsolete "Use ``at'' instead" is
            -- Entry of index i.
            -- Applicable only if is between currently defined bounds.
        require
            index_large_enough: lower <= i;
            index_small_enough: i <= upper
        external
            ...
        do
            ... Instructions ...
        end; -- entry

    Other than for its ``obsolete'' clause, such a routine is a normal one.
It may or may not appear in the ``export'' clause of the class (this one
does).

    Only in two ways do the properties of an ``obsolete'' routine differ
from those of a non-obsolete one:

    - The ``short'' command, an essential tool of the Eiffel environment,
    producing class interface documentation automatically from the
    text of a class, will not show obsolete routines in its output,
    whether or not such routines are exported.

    - The Eiffel compiler (commands ``es'' or ``ec'') will flag the first use
    of any obsolete routine in a system by outputing a warning of the form

        Warning: routine xxxx is now obsolete.
        Message

    where ``Message'' is the string that has been given in the
    ``obsolete'' clause, if any. In the above example, this message tells
    the programmer of a dependent class (client or descendant) that the
    routine ``at'' is now the preferred way to access an array element,
    although ``entry'' remains valid.

The compiler messages may be turned off if desired through an option.

    The use of obsolete routines by dependent classes has no effect
other than the above warning. The understanding, however, is that obsolete
features will be removed from the library classes in some later release.
Programmers are given ample time to update the dependent classes.

    Because they cease to be documented in the official reference,
obsolete routines pose no immediate threat to the complexity of the class
as perceived by new users. (This is different from what would occur if
both old and new features were kept as mere synonyms.)

    Several variants to the above syntax were considered during the design
of this facility (as well as a convention that would have been outside the
language proper, such as keeping a list of obsolete features in a special
file).  The solution retained seems to be the most convenient. The
modification of an existing class to make a routine obsolete is easy:
just add the ``obsolete "Message"'' clause, leaving everything else unchanged.
(It may also be a good idea to move the routine to the end of the class.)

    Rather than an explicit ``Message'' it could have seemed preferable
just to include a reference to the replacing routine, since in practice
many actual ``Messages'' will be of the form exemplified above
(``Use xxxx instead''). This, however, would not have taken care of case 3
(where the recommended replacement is a routine with a different
signature, as in the add_subwindow example); it is also not applicable to
case 2 (a routine for which the recommendation is simply not to use it any
more).

	As a minor drawback of the introduction of the ``obsolete'' facility,
the ``short'' command is somewhat slower in version 2.2 than in previous
releases; previously it relied on a one-pass analysis of the text, but now
it must first check for the presence of any obsolete feature. The
penalty should be acceptable in practice. (We considered alternative
forms of the facility that would have averted this problem by declaring
all obsolete features at the beginning of the class, but this turned out
to be inadequate conceptually.)

    The ``obsolete'' facility is not a panacea. As we have seen, it is not
applicable when you find a real design mistake (for which the only
solution is to correct the mistake) or for an in-depth redesign of a
class (such as has been applied to the entire Eiffel Graphics library
for version 2.2). It is also not appropriate when you want to provide
two or more synonyms for a given feature, none of which
is obsolete, as in the example discussed in a previous message
(<137@eiffel.UUCP>) where ``top'' and ``at'' are synonyms in STACK classes.

    As it stands, however, I hope this facility will help us provide a
smooth transition path as we continue to refine every possible detail of
the Eiffel Libraries. I also hope that it will prove useful to developers
of other libraries of reusable Eiffel components. 

-- Bertrand Meyer
bertrand@eiffel.com

keithc@atlantis.UUCP (Keith Campbell) (05/12/89)

In article <138@eiffel.UUCP> bertrand@eiffel.UUCP (Bertrand Meyer) writes:

>    Situation 3 may occur (among other cases) when you realize that a
>routine has too many arguments and should be split into two or more
>routines.

>    w.add_subwindow (other_window, horizontal_position, vertical_position) 

So, the feature invocation above must be replaced by two calls:

     w.add_subwindow( other_window );
     other_window.move( horizontal_position, vertical_position ); 

Is the old version of ``add_subwindow'' (with three arguments) still
available? If so, this violates the rule that a class has at most one
feature of a given name.  If not, it would seem that in this case the
``obsolete'' facility does nothing for the "early" users of this class.

-- 
Keith Campbell               Cognos Incorporated   S-mail: P.O. Box 9707
Voice: (613) 738-1338 x5222                                3755 Riverside Drive
  FAX: (613) 738-0002                                      Ottawa, Ontario
 UUCP: uunet!mitel!sce!cognos!keithc                       CANADA  K1G 3Z4

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

From <6099@atlantis.UUCP> by keithc@atlantis.UUCP (Keith Campbell):

 > Is the old version of [a routine being replaced by a version with a
 > different interface] still available? If so, this violates
 > the rule that a class has at most one
 > feature of a given name.  If not, it would seem that in this case the
 > ``obsolete'' facility does nothing for the "early" users of this class.

	Clearly in this case the new feature should have a different name.
This should have been explained clearly in my original note.
-- 

-- Bertrand Meyer
bertrand@eiffel.com

keithc@atlantis.UUCP (Keith Campbell) (05/16/89)

In article <143@eiffel.UUCP> bertrand@eiffel.UUCP (Bertrand Meyer) writes:
->From <6099@atlantis.UUCP> by keithc@atlantis.UUCP (Keith Campbell):

-> > Is the old version of [a routine being replaced by a version with a
-> > different interface] still available? If so, this violates
-> > the rule that a class has at most one
-> > feature of a given name.  If not, it would seem that in this case the
-> > ``obsolete'' facility does nothing for the "early" users of this class.

->	Clearly in this case the new feature should have a different name.
->This should have been explained clearly in my original note.

This is not always possible.  In fact, it is not possible to rename the
feature in your window example: Create.  What do we do now?

-- 
Keith Campbell               Cognos Incorporated   S-mail: P.O. Box 9707
Voice: (613) 738-1338 x5222                                3755 Riverside Drive
  FAX: (613) 738-0002                                      Ottawa, Ontario
 UUCP: uunet!mitel!sce!cognos!keithc                       CANADA  K1G 3Z4

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

From <6121@atlantis.UUCP> by keithc@atlantis.UUCP (Keith Campbell):

> [It] is not always possible [to let an obsolete feature remain available
> under a different name]. In fact, it is not possible to rename the
> feature in your window example: Create.  What do we do now?


	Indeed this does not work for Create this there is exactly one Create
procedure per class.

	A later posting on changes planned for Eiffel version 3 (late 1989)
will explain plans for removing this requirement on Create
procedures. Currently, however, if the interface of Create is changed
there is now way to avoid adapting clients.
-- 

-- Bertrand Meyer
bertrand@eiffel.com

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

Article <144@eiffel.UUCP>, line 8, should have read:

  Indeed this does not work for Create since there is exactly one Create...
                                       ^^^^^
Sorry for the typo.

-- Bertrand Meyer
bertrand@eiffel.com