bertrand@eiffel.UUCP (Bertrand Meyer) (07/10/89)
This document describes the innovations brought by Release 2.2 of Eiffel. A non-technical comment: Over the next three weeks all current Eiffel installations will be contacted for a check of delivery information (operating system, maintenance status etc.). ------------------------------------------------------------- WARNING: The paper version of this document (distributed with the release) makes generous use of fonts. The following can only be an approximation and some aspects may consequently be difficult to understand. Also, because of the 50K limit imposed by some systems, this posting has been split into two parts. (Since we were just over 50K, the second part is quite small.) ------------------------------------------------------------- RELEASE 2.2 OVERVIEW Bertrand Meyer Jean-Marc Nerson Interactive Software Engineering Inc. 1 OVERVIEW Release 2.2 of Eiffel is a major set of improvements reflecting the expanded use of Eiffel for ever more ambitious applications. The improvements affect the following areas. + Complete rewrite of the X-based Graphics Eiffel Library. + Extended support for persistent objects. + New libraries: class browsing, parsing, lexical analysis, non-graphical windowing. + Numerous improvements to the Basic Library, use of a uniform terminology for features, better file management facilities. + New tools, in particular the Eiffel Browser eb for interactive exploration of available classes. Improvements to existing tools such as the Eiffel viewer. + Full implementation of repeated inheritance. + Language extensions making Eiffel more flexible: ``Unique'' values and ``Inspect'' instruction for multi-branch choices, ``Accept'' instruction for type-safe assignments going against the inheritance hierarchy, support for composite objects, ``define' clause to merge deferred and effective routines, ``obsolete'' clause to phase out library features gently, ``indexing'' clause for systematic indexing of reusable software components, new boolean operators. (No change is made to existing constructs.) + Numerous individual improvements, performance boosters affecting the tools of the environment as well as the efficiency of the generated code, and bug fixes. + Improved documentation, new reference manual. Section 2 of this document covers enhancements to the Basic Libraries. Section 3 explains the new browsing facilities. Section 4 describes support for persistent objects. Section 5 introduces the new language extensions. Section 6 addresses improvements to the tools of the environment. Section 7 lists bug fixes and areas where performance has been improved. Section 8 presents new documentation. Section 9 discusses compatibility issues of compatibility with pre-2.2 versions. 2 LIBRARIES 2.1 Structure The library is now divided into six parts (each of which comes as a directory, with its own doc directory): + Kernel: classes such as ARRAY, STRING, EXCEPTIONS and others which provide essential facilities. + Structures: fundamental data structures (lists, queues, trees etc.). + Graphics: graphical library. + Winpack: non-graphical window management. + Parsing: lexical and syntactic analysis, compiler writing. + Support: Access to internal Eiffel facilities. In addition, a seventh directory, called 2.1, contains a full library functionally equivalent to the previous (2.1) library, enabling previously written Eiffel systems to be compiled and run exactly as before. Minor adaptations may be needed for these applications to work under the new libraries (see section 9 on compatibility issues). 2.2 Graphics The graphics library has been entirely rewritten to use X Windows version 11.3. Many more facilities are provided than with the 2.1 version; in general, the graphics library is now at the same level of robustness as the rest of the environment. The X dependencies are isolated in specific classes to facilitate future adaptations of the library to other graphical bases. The X-based tools work much better than in the past with the rest of the X system; in particular, they work across a network (with a client-server architecture) and with any display manager. Several graphical demonstration systems are provided in source form to serve as models for use of the library. They include the GOOD graphical browser (see below), which has been considerably improved with new facilities, a better interface, mouse tracking for moving class bubbles etc. A set of C routines interfacing Eiffel and X has been included in the Eiffel delivery to help cross-developing portable X-based applications. The MIT X Windows package itself is not included in the delivery any more, as it is expected that a majority of installations will have independent access to X. The graphics library and tools require that X version 11.3 be installed. 2.3 Support The ``support'' library includes facilities for persistent objects (ENVIRONMENT, STORABLE, see below), access to the internal structure of Eiffel objects (INTERNAL), advanced memory management (MEMORY) and browsing (E_CLASS, E_WORLD, see below). Class MEMORY now includes a procedure dispose with no argument and an empty body. This procedure is invoked by the garbage collector (if enabled) when it reclaims an object. A programmer wishing to have a particular action executed whenever an instance of a certain class is reclaimed (for example, if the object was associated with a file, closing that file) may write the class as a descendant of MEMORY and redefine dispose appropriately. Class EXCEPTIONS (a support class, but included in the kernel library for technical reasons) now includes a procedure continue. By inheriting from this class and redefining the procedure, a class design may ensure that processing can continue at the point of interrupt (after designated handling) for certain exceptions. 2.4 New libraries Two new libraries are provided. The parsing library provides a set of predefined classes for object-oriented parsing, with the supporting tools for lexical analysis. The parsing classes make it possible to write parser-based tools (compilers, interpreters, static analyzers and the like) with a natural and modular structure, based on classes which correspond to grammar productions. Semantics can be added easily using multiple inheritance. The winpack library supports the development of window-oriented applications on character-oriented (rather than graphical) terminals. It is enough for the terminal to have the proper ``termcap'' descriptions. The facilities supported include hierarchically nested windows, cursor movement, echoed and non-echoed input in ``raw'' and ``cooked mode'', text manipulation in windows and many others. The basic class is WINDOW. An heir of this class, called EDITOR, provides the functionality of a simple window-oriented text editor. 2.5 Data structure library The data structure library has been extended with new abstractions such as chains, ordered sets, priority queues etc. The FILE class (now part of the kernel library) has been extended with new input-output facilities and better handling of erroneous cases. 2.6 Terminology and consistency A systematic effort has been undertaken to use a consistent terminology throughout the data structure and other libraries. For example, the basic operation for inserting an element into a data structure is called put, replacing enter (for arrays), push (for stacks), add (for queues), insert (for hash tables) etc. The basic operation for accessing an element is called item, replacing entry (arrays), top (stacks), oldest (queues) etc. The signatures (types of arguments and results) are not the same in all cases, but the name is the same if the purpose is similar. The old names are still available as ``obsolete'' features (see below). The consistency of the library has also been improved in other ways, particularly by introducing more deferred classes to capture common properties, by including more assertions, and by adding new generic parameters to certain classes to make them more general. As an example of the last case, the old H_TABLE [T] class described hash tables; the keys used to insert and retrieve elements had to be character strings. This may be too restrictive. Consequently, the new class HTABLE [T, U -> HASHABLE] uses a generic parameter U representing the type of the keys. U is only constrained to be a descendant HASHABLE, a deferred class which includes a hash deferred function to be used for hashing keys. STRING, of course, inherits from HASHABLE. The new old H_TABLE, kept for compatibility, has been adapted so that it now inherits from HTABLE [T, STRING]. 2.7 ANY and HERE Two new kernel classes, ANY and HERE, offer universal facilities which are automatically made available to every class. These classes play a special role, as any programmer- defined class now inherits from HERE; HERE is itself an heir to ANY. ANY includes routines which are potentially useful for any class: + The string-valued function out is such that a.out, for any a, contains a printable representation of a (which may be an object or a value of a basic type). + The procedure copy is such that a.copy (b) copies the contents of the object associated with b onto the object associated with a. Both objects must exist (otherwise an exception is raised). Procedure copy is similar to the predefined feature Clone, but its target is an existing object rather than one created for the occasion. + Routines deep_copy and deep_equal provide for recursive copy and recursive equality test of complex structures. Class HERE, as delivered, is empty except for its ``inherit ANY'' clause. It is meant for defining universal facilities that are considered useful by a certain programmer, a certain group or a certain company. In contrast, ANY should normally be kept unchanged. 3 BROWSING An important set of new facilities allows for browsing, or exploration of existing classes and their properties. These facilities are all implemented in terms of the features of the support library class E_CLASS and a few related classes. This means that any Eiffel application may query the environment about classes and their properties by accessing the features of these classes. An instance of E_CLASS describes an Eiffel class and gives access to its features selected according to various criteria (attributes, routines, functions, deferred routines etc.), its parents and ancestors, heirs and descendants, clients, suppliers, invariant and so on. Two interactive tools give users access to these facilities. One is eb (Eiffel Browser), which will work on any character-oriented terminal. The other is GOOD, which runs on X Windows, is mouse-driven and provides the same information in graphical form (bubbles and arrows). GOOD (the acronym means ``Graphics for Object-Oriented Design'') also supports further facilities for creating new classes through graphical interaction. Both eb and GOOD are delivered in source form. This provides Eiffel programmers with complete examples of advanced use of the class browsing facilites from class E_CLASS. 4 PERSISTENCE In pre-2.2, persistence was supported by class STORABLE and its two routines store and retrieve. The former would store an object and all its dependents (the objects to which it contains references, directly or indirectly); the latter would retrieve the whole thing. These facilities continue to be supported. More advanced techniques are now available, however, through the support class ENVIRONMENT. An environment (instance of class ENVIRONMENT) is a set of objects. At any time during execution, one environment is active; the active environment may be changed. Every object, as it is created, becomes part of the currently active environment. Furthermore, some objects may be given keys with respect to the environment they belong to. A key is a string. An environment may be stored into a file; this means that all the objects of the environment, and their dependents, are also stored. The environment may then (in the same or another session) be loaded back. Then individual objects of the environment may be selectively retrieved if they have been given keys. An environment is stored either explicitly (by env.store (f)) or by executing at initialization time one of the calls env.store_on_end (f) or env.store_on_failure (f), which specify that the entire environment should be stored automatically when the program terminates normally or abnormally. These facilities represent a first step towards including within Eiffel a full-fledged object-oriented database system, and for removing the conceptual distinction between those objects which are in main memory and those that reside elsewhere (possibly coming from environments stored in previous sessions), in the spirit of virtual memory. 5 LANGUAGE EXTENSIONS 5.1 Expanded types Expanded types provide a more flexible dynamic object model, where objects can have sub-objects, not just as references to other objects. An entity may now be defined as being of an ``expanded'' type, meaning that its value will be an object, not a reference to possible objects. This makes it possible to avoid unneeded indirections and provides a uniform framework for describing class types and basic types (INTEGER etc.). Another advantage is the ability to share information with external languages such as C in a more flexible way. As a consequence of the more flexible dynamic model, a DOUBLE expanded type (double precision reals) is now supported. A new set of predefined types is introduced: BITS M, for any non-negative integer constant M, describes objects whose value fits in M bits. All class types (for which entities represent references) fit in 32 bits; so do INTEGER and REAL. DOUBLE fits in 64 bits. Associated techniques extend the generic facilities. They are described in a separate document. These extensions make the Eiffel type system at the same time more consistent, more powerful and more flexible. 5.2 Infix and prefix operators A routine can now be defined in such a way that calls to the routine will use an operator in prefix or infix form. For example, an addition routine in a class MATRIX can be named infix "+", so that calls will be of the form a + b. Previously, the routine would have been named for example plus, with calls of the form a.plus (b) As an example, class COMPARABLE from the kernel library now contains the following declaration: infix "<=" (other: like Current): BOOLEAN is -- Is current element less than or equal to other? deferred end; -- "<=" Only the predefined operators of the Eiffel grammar (arithmetic, boolean and relational operators) can be used in infix or prefix form. They are the following: + - * / < > <= >= and or xor not div mod Of these, only +, - and not may be used as prefix. All except not may be used as infix. The precedence of all these operators is fixed, and remains as given in the grammar for pre-2.2 Eiffel (where they were applicable to operands of basic types only). A routine declared in infix or prefix form is like a normal routine whose name, instead of being an identifier, would be of the form infix "operator". This name may appear wherever a routine name is legal, for example in the routine definition itself (as for "<=" in the above declaration), in a rename clause (such as rename union as infix "+", infix "<" as smaller), an export list or a redefinition clause. For routine calls, however, the syntax uses the infix or prefix form of the operator instead of the usual target.routine_name (...) form. The infix-prefix mechanism is only a syntactic facility. It should help produce programs that are both nicer to write and nicer to read. 5.3 Repeat clause for export lists The ``repeat'' clause makes it easier to write and manage export clauses. It is applicable to cases when the exports of a class include all the exports of one (or more) of its parents. According to the Eiffel rules on inheritance and information hiding, a class B that inherits from a class A is not constrained to export all the features exported by A. When this is the case, however, the repeat subclause provides a convenient syntactical abbreviation. The presence of repeat A in the export clause of B has the same meaning as a textual copy of the export clause of A. For example, here is the beginning of class FIXED_STACK in the structure library: -- Stacks with a fixed physical size, implemented as arrays. class FIXED_STACK [T] export repeat STACK, max_size inherit STACK [T] define empty, nb_item ARRAY [T] rename ... redefine ... Here FIXED_STACK exports all the features that are exported by its parent STACK. Additionally, it exports feature max_size. A repeat A subclause appearing in the export clause of B is only permitted if B is an heir (not an indirect descendant) of A. Its is equivalent to a list of features, some with export restrictions, copied from the parent. The export clause of the heir may include other elements, as with max_size in the above example. In the case of multiple inheritance, there may be more than one repeat subclause. The advantage of the ``repeat'' facility is not only that export lists are shorter but also that any change in the export list of the parent will be automatically propagated to the heir. This brings an important simplification to the management of export lists. 5.4 Define subclause The new define subclause, part of the inherit clause, makes it possible to implement one or more deferred routines by merging them with an effective (non-deferred) routine or attribute. As an example, both the ARRAY kernel library class and the LIST structure library class include an integer function called nb_items, giving the number of items in an array or list. This function is is effective in ARRAY, where it returns upper - lower + 1 (it could also have been implemented as an attribute); it is deferred in LIST. The former provides the appropriate implementation for the latter in class FIXED_LIST (lists with a fixed number of items, implemented as arrays), which begins as follows: class FIXED_LIST [T] export repeat LIST inherit LIST [T] redefine ... define nb_items ARRAY [T] rename ... In pre-2.2, the same effect would have been achieved as follows: class FIXED_LIST [T] export ... inherit LIST [T] redefine ... ARRAY [T] rename nb_items as array_nb_items, ... -- Renaming is necessary to avoid a name clash feature nb_items: INTEGER is -- Number of meaningful items in list do Result := array_nb_items end; -- nb_items ... The improvement in ease of expression is significant: it is no longer necessary to include a trivial function definition; nor is the renaming of nb_items for arrays needed any more, since the name clash in this case is in fact desired. There is also a performance improvement, since nb_items for FIXED_LIST is now the same code as the corresponding function for ARRAY, avoiding an extra level of routine calls. The rest of this section describes the precise syntax and semantics of the define clause. For the semantics, it is useful to introduce the notion of ``final name'' of a routine The final name of a routine in a class is the routine name if the routine is introduced in that class, or inherited from a parent and not renamed. If the routine is inherited under a new name, the final name is that name. The define subclause may appear in one of the inherit clauses of a class C after the redefine subclause, if any, and before the rename subclause, if any. The keyword define must be followed by a list of final routine names, all of which are inherited deferred routines. For each such routine r, there must be an effective feature (routine or attribute) in C, with the same final name and the same signature as r. This effective feature may be either declared in C or inherited from a parent; it serves as implementation of r in C. (Hence r is effective in C.) If two or more of the features of C have the same final name, then C would have been illegal in pre-2.2 because of the name clash. In 2.2, however, if all of these routines are deferred except at most one, the deferred ones all appear in define subclauses, and the conditions for their correct implementation by an effective feature are met, then no illegal name clash will now be diagnosed. In the simplest case, C inherits only one deferred routine called r from its parents, and provides an effective definition for r: class C export ... inherit inherit P define r feature r is -- The effective implementation do ... end; -- r ... This is the standard case of providing an effective declaration for an inherited deferred routine. Here the inclusion of r in the define subclause for P is optional. This is compatible with pre-2.2 Eiffel. The define subclause is particularly useful when, in a multiple inheritance case, a class implements a deferred routine inherited from a parent by an effective routine inherited from another. Assume C inherits from A and B, and we want to implement r, a deferred routine inherited from A, by an effective feature inherited from B. If the names are the same (as in the nb_items example), it suffices to list r in the define subclause for A: class C export ... inherit A define r ...; B ... The define facility can also be used, however, if the name of the defining effective feature is different, say s. What is required, as seen above, is that the final names should be the same. This may be achieved by renaming either the effective or the deferred routine. For example: class C export ... inherit A define r ...; B rename s as r ... The define subclause simplifies a number of cases of multiple inheritance and has been fruitfully applied to the libraries. 5.5 Accept instruction The Accept predefined feature makes it possible to perform assignments going against the static inheritance rules if the assignments are type-safe at run time. The instruction is written as follows: x.Accept (y) where x is an entity and y is an expression, and is syntactically correct if and only if the static type of x, say D, conforms to the static type of y, say A. (The static type is the type with which an element is declared; a type conforms to another if it is one of its descendants, with special provisions for inheritance.) In this case, the type rules of the language permit the assignment y := x, but not the reverse one. Accept makes the reverse assignment possible, but only when it is dynamically found to be compatible with the type system. In other cases, the value of x will become a void reference. The effect of the instruction may be defined by the following pseudo-Eiffel extract: if not y.Void and then ``The type of the object associated with y conforms to A'' then ``x := y'' -- This assignment cannot be written as such -- since it violates the type rules. else x.Forget end In other words, the instruction assigns to x a reference to the object referred to by y if this object exists (not y.Void) and its type is a descendant of the type of x; otherwise, it makes x a void reference. In contrast with := assignment, whose validity is determined at compile-time by examining the static type of the source and target, the validity of the Accept instruction depends on the dynamic type of the source (that is to say, the type of the associated object). This form of controlled reverse assignment is essentially useful in connection with generic structures and persistence. Assume that RECTANGLE inherits from POLYGON; the former class introduces a new feature diagonal. Assume that during your last session before your one-year around- the-globe sailing trip you ran an execution of a system containing the entity pstack: LINKED_STACK [POLYGON] and pushed a number of polygons onto it by calls of the form pstack.put (p). (Feature put is the ``push'' operation for stacks). Because of the basic type rules, some of these p may actually have been of type RECTANGLE. Before you left you stored away the pstack structure in a file, using the persistence facilities described in section 4. As you come back you use the associated facilities to retrieve this stack. Its top stack may be obtained through: p := pstack.item where item is the feature from LINKED_STACK which returns the top of a stack. Assume now that you remember (or think you remember) that the last object you pushed on the stack before you took off was actually a rectangle and you absolutely need to obtain the diagonal of that rectangle. Unfortunately, the type of pstack is LINKED_STACK [POLYGON], so that item here returns a result of type POLYGON. Then entity p as used in the above assignment, must also be of this type (or an ancestor) and the call p.diagonal would be illegal. The Accept facility may be usefully applied in such a case: pstack: LINKED_STACK [POLYGON]; p: POLYGON; r: RECTANGLE; rdiag: REAL; ... p := pstack.item; r.Accept (p); if not r.Void then rdiag := r.diagonal else -- p was void, or else not a rectangle ... Appropriate treatment .. end This technique is safe: if your memory failed you and the stack top is not in fact a RECTANGLE object, no incorrect application of diagonal may result. Most uses of the Accept facility should be structured in this fashion, with a conditional instruction after the Accept to check if the target has indeed been assigned a proper reference. In short, the Accept instruction supports assignments for cases in which the static inheritance rules would not permit normal assignment but the dynamic type of the source object make the operation meaningful. The operation is safe (no incorrect conversion may result); the programmer can control it by testing for voidness. The instruction provides a way to test for the dynamic type of an object. Usually, however, there is no need to do so in standard cases of Eiffel programming; the implicit form of testing provided by dynamic binding is much prefereable to explicit testing, as explained in detail in Object-Oriented Software Construction. Accept does provide a useful complementary mechanism in cases when one does need to find out the dynamic type. Usually this should only arise in the context of object persistence, as in the above example: when objects are retrieved from a previous execution of an Eiffel system, the type information contained in the source text of that system has been lost. 5.6 Unique values A new form of constant integer attribute, declared as ``unique'', is provided. This makes it possible to let the compiler devise a set of integer codes that are guaranteed to be different and may then be used in multi-branch choice instructions. The form of a ``unique'' declaration is: a, b, c, ...: INTEGER is unique The effect of such a declaration is the same as the effect of a: INTEGER is V1; b: INTEGER is V1; c: INTEGER is V1; ... where V1, V2, V3, ... are different integer constants. The difference, however, is that the values of these constants are chosen by the compiler, not the programmer. They are guaranteed to be different for all ``unique'' attributes introduced within a class. The uniqueness is not guaranteed across classes; in particular, a unique attribute introduced in a class may have the same value as a unique attribute inherited from an ancestor of that class. This facility relieves the programmer from having to assign integer codes manually when there is a fixed list of possible situations. In other words, the declaration everest, mont_blanc, mc_kinley: INTEGER is unique; replaces the more tedious: everest: INTEGER is 1; mont_blanc: INTEGER is 2; mc_kinley: INTEGER is 3; Corresponding multi-branch tests may then be made using the inspect instruction, as seen next. 5.7 Inspect instruction The inspect instruction is a new control structure for multi-branch choices, avoiding more complicated if... then with a fixed set of choices and is not a substitute for inheritance and dynamic binding. The syntax of the construct is: inspect e when V11, V12, ... then instructions when V21, V22, ... then instructions ... when Vn1, Vn2, ... then instructions else instructions end Here e is an expression of type INTEGER or CHARACTER; V11, V21, ..., V21, etc., called ``inspect constants'', are constants of the same type as e; instructions represents a sequence of zero or more instruction separated by semicolons. The else instructions part is optional. The inspect constants may be explicit constant values or constant attributes; in the latter case they may have been declared as ``unique'', and must all have been introduced in the same class, so as to be guaranteed to have different values. (If any is declared as ``unique'', then all must be.) If they are not ``unique'', their values must also be all different. For inspect constants declared explicitly (not ``unique''), a notational facility is offered: a sequence of consecutive explicit values, such as 3, 4, 5 or 'a', 'b', 'c', 'd' may be abbreviated as an interval, written in the form min..max, as in 3..5 'a'..'d' If, when e is evaluated, its value is one of the Vij constants in one of the when clauses, then the effect of the inspect instruction is to execute the corresponding instructions right-hand side. (The rules ensure that there is at most one such when clause.) If no when value matches the value of e, then the else clause is executed if present; otherwise an exception is raised. The symbolic name for the corresponding exception code in the library class EXCEPTIONS is Invalid_inspect_value. The inspect instruction provides support for multi- branch discrimination for a fixed set of choices. Like traditional ``case'' instructions, however, it is a closed construct: adding new cases may require important modification in client code (see the ``open-closed principle'' in Object-Oriented Software Construction, section 2.3). Any situation in which some non-trivial operations must be performed as a result of the discrimination, or new cases are expected to arise, should be handled not by writing multi-branch choices but by using specific object-oriented techniques: writing a set of classes that inherit from a common ancestor and redefining in each of these classes the routine performing the variable operation. Then, thanks to dynamic binding, the call a.r will select the appropriate version based on the run-time form of a. The inspect mechanism is not appropriate for such situations. 5.8 Obsolete routines The ``obsolete'' facility provides a way to phase out routines from a library class without immediate impact on current users. A clause of the form obsolete Message may be added before the is in the declaration of the routine. The message is a string constant in quotes. As an example, it was noted above that the procedure for changing an array entry is now called put rather than enter. Existing classes, however, will still use the old name. This name is available as an obsolete feature name in the new version of library class ARRAY: enter (i: V; v: T) obsolete "Use ``put (v, i)''" is do put (v, i) end; -- enter This declaration actually appears not in ARRAY, but in one of its parent, the kernel class INDEXABLE [T]. The message should indicate the suggested replacement. Often it should just be of the form Use "new_name" instead; in cases such as the above, it also indicates that the order of arguments has changed. A routine declared as obsolete has all the properties of a normal routine with two exceptions: + Obsolete routines never show up in the output of the short class abstracter. + When the compiler (commands es or ec) detects that a client or descendant of a class C uses an obsolete feature of C, it produces a compile-time warning of the form ``Feature name is obsolete; Message'', where Message is the message defined for the obsolete routine. Warnings can be disabled through the -w option of either compiling command. Only a routine can be obsolete, not an attribute. This is because the presence or absence of an attribute affects the run-time structure of every instance of the class. To obtain the same effect as if attributes could be made obsolete, do the following on an attribute a introduced in class C: + Choose a fresh attribute name, say obsolete_a, not used for any feature in C. + Replace all occurrences of the name a in C by obsolete_a. + Declare in C a new argumentless function called a, with a result of the same type as obsolete_a. The body of this function is just Result := obsolete_a. Declare this function as obsolete. + The interface of C is not changed; nor is the run- time structure of its instances. Clients and descendants are still valid. There is a performance cost, however, since outside accesses to a now imply a function call. Thanks to the ``obsolete'' facility, libraries can be refined and improved without undue immediate impact on programmers. Without this facility, the effort applied to the Basic Eiffel Library, in particular the revamping of feature names (section 2.6 above), would not have been possible. In the Basic Library, the ``obsolete'' features will be removed from any version distributed ten months or more after the features first became obsolete. 5.9 The ``indexing'' clause The ``indexing'' clause is meant to help archiving classes, retrieving them and browsing through them. This is a new optional clause, which may be included at the beginning of a class. Its syntax is illustrated by the following example: indexing author: Jane_Smith; keywords: hash, hashing, table, searching; date: March, 1989; revision_1: July, 1989 class HTABLE [T] export ... (etc.) The labels (such as ``author'') are identifiers; the right-hand side are elementary items (integers, identifiers etc.) separated by commas. The purpose of this facility (remotely inspired by the Cobol ``identification division'') is clear: allow for indexing of reusable software components by sophisticated programming environment tools such as indexers and browsers. Although, in the Eiffel tradition, most of the documentation about a class is in the standard class text itself, some extra information may be useful. The ``indexing'' clause will contain this information. For release 2.2, the tools of the Eiffel environment do not take much advantage of this facility. It has been included as a way to prepare for future enhancements, which will be particularly useful as the body of Eiffel classes available from various sources grows, paving the way for a full-grown industry of reusable software components. 5.10 New boolean operators Two new boolean operators are available: xor, for exclusive or, and implies. The definition of implies is that a implies b is the same as not a or else b This operator is especially useful in class invariants and other assertions. Numerous invariant clauses of the data structure library which used an or or or else form have been rewritten more elegantly using implies. 5.11 Interface with external routines The interface between Eiffel and other languages, especially C, has been made more flexible in several ways. The presence of expanded types makes it possible to exchange structures, not just one-word items, with other languages. An external routine can be called with the address of an Eiffel routine r as actual argument. The argument is written @r. The external routine (which may for example be a call-back mechanism) may then call r. Other facilities are provided for a C routine to call Eiffel routines and create Eiffel objects. 6 ENVIRONMENT TOOLS A number of significant improvements have been made to existing tools. 6.1 Compiler (es, ec) The compiling commands es and ec have been improved in a number of ways. This section addresses the functional improvements; performance improvements are discussed below. There is no limitation on the number of classes of a system. (A given run of es or ec, however, can compile at most 500 new classes, which should not be a problem in practice.) Command ec will now read all its options in the System Description File (which must be called .eiffel in the current directory) if there is one. Any command-line option overrides the corresponding SDF option. Command ec has a new option: -gi (i = 1, 2, 3 or 4). The effect is to stop after pass i of the compiler. By default, the compiler assumes that the kernel library is in a directory fixed at installation time. This can be overridden by specifying another directory in the new KERNEL entry of the SDF, or, for ec, through the -k command-line option. The SDF entry listing directories for needed classes is now labeled UNIVERSE rather than SOURCE (the latter name still works). Both es and ec now have a -w option which suppresses printing of compile-time warning messages. A LINKER environment variable is now available, corresponding to the linker to be used by es when option -l is selected. 6.2 Run-time behavior Two changes have been made to the run-time behavior of compiled systems. The first change applies to the effect of a particular exception. Normally, when an exception occuring at run time is not explicitly caught by a rescue clause of some routine or class, a complete ``exception history table'' is displayed (on the standard output), showing a log of all exceptions having occurred during the execution of the system. This useful facility can be a nuisance in cases when the exception was simply caused by the user typing the BREAK character (control-C) to stop execution. This case is now recognized and leads to silent termination, without any exception history table being produced. Of course, the corresponding exception can still be caught explicitly by the software. The other change applies to the effect of the TRACE debugging option for step-by-step execution tracing. For classes compiled with this option on, the trace now indicates at each line the number of the call's source line in the corresponding class. 6.3 Short and flat Command short, the class abstracter and documenter, has two new options: -i, to produce an index, and -a. Option -i produces an index of all classes processed by short and all the exported features of these classes; troff will include the proper page numbers. This has been used to provide the library manual with a comprehensive index. Option -a, which is also applicable to the inheritance flattener flat, prints exported features inherited from class ANY (the default is not to print them since they are in all classes). If a repeat clause is present in a class (section 5.3), short will attempt to expand it; this requires that the class have been at least partially compiled. If not, short will keep the repeat clause unchanged. The command has been updated to ignore ``obsolete'' features (section 5.8). 6.4 The viewer The viewer is a run-time object inspector which quadruples as interactive debugger, fake Eiffel interpreter and interactive tester. A number of facilities have been added to the viewer; in particular: + Routines called interactively can have references (of class types) as arguments; assignments to references are also possible. + The objects encountered during a viewer session are remembered and can be given human-significant names. + Input and output can be freely redirected. 6.5 C package generation The postprocessor is the part of the compiler used to produce self-contained C packages for cross-development and to perform in-depth optimizations. In pre-2.2, different C packages produced from different Eiffel systems could not coexist without some tedious and non-trivial hand adaptations. It is now possible to generate a package which includes an external archive resulting from a previously produced C package. 7 BUG FIXES 7.1 Repeated inheritance Repeated inheritance occurs when a class inherits more than once from the same ancestor. This is often a consequence of multiple inheritance (D inherits from B and C, both of which inherit directly or indirectly from A.) The rules for repeated inheritance are described in section 11.6 of Object-Oriented Software Construction; they imply that features (routines and attributes) which are inherited more than once are duplicated or shared depending on whether they have been renamed or not. In version 2.1, the rules had been implemented for routines but not yet for attributes. The full repeated inheritance rules are now supported. 7.2 Performance The performance of the tools has been improved in a number of areas. The efficiency of the run-time system has been significantly improved in both time and space. The tables it uses have a much smaller size, especially in the case of large systems. The code has also been made more compact. The run-time has been split into a number of pieces; only the needed pieces are actually loaded, depending on the options selected (for example garbage collection or not). The startup time for es, especially for large systems, has been significantly decreased (by a factor of two or more). Code generated with option NO_ASSERTION_CHECK on is more compact and has a shorter startup time. The inheritance flattener flat is about twice faster. The low-level routines used for input and output in class FILE are significantly faster. In general, the performance of input and output primitives from this class is considerably better thanks to the memorization of more file information. When compiled under NO_ASSERTION and OPTIMIZE, the postprocessor uses an internal compilation option which removes useless data, reducing the size of executable files. 7.3 Miscellaneous A number of problems have been corrected. The use of a reserved word as identifier produces a much clearer error message. Using Result in a procedure causes an error message. A remote call of the form a.b....Clone (...), with more than one dot, causes an error message. (As with Create, this violates export restrictions; the supplier should include an exported procedure that performs the cloning.) The violation of a precondition triggers an exception in the caller, not the called routine (in accordance with the ``programming as contracting'' philosophy). The types of external routines arguments can be declared, in accordance with the syntax given in Object- Oriented Software Construction. The inheritance flattener flat keeps comments coming right after an attribute (this is particularly useful if short is applied to the result) and class invariants. A bug regarding the use of the C_COMPILER environment variable has been fixed. The -e (explain) option for es and ec prints out information on calls to cc (C compiler). Commands es and ec do not fail any more in the case of deep relative paths between the current directory and other needed directories. Cycles in the inheritance graph are detected in all cases. The exception history table contains adequate information when the post-processor (C package cross- generator and optimizer) is used, and when repeated inheritance is used with routine duplication. The postconditions of some routines in the data structure library classes FIXED_QUEUE and LINKED_QUEUE have been corrected. Bugs in data structure library classes H_TABLE and BOOLEAN_SET have been corrected. A rare but annoying bug in the garbage collector (occurring for routines creating many objects and using expressions referring to objects returned by functions) has been corrected. Exceptions occurring during the processing of an exception are caught properly and cause immediate termination, with a clear trace history being printed if possible. Tools es and ec are fully consistent with each other. An extra semicolon after the last parent declaration in an inherit clause is now harmless. Constrained genericity works better in some complicated cases. The screen-oriented tools such as the viewer and eb now understand padding in Termcap entries and have better error recovery when the TERM and TERMCAP environment variables are not properly set. END OF PART 1 (OF 2) -- -- Bertrand Meyer bertrand@eiffel.com
bertrand@eiffel.UUCP (Bertrand Meyer) (07/10/89)
PART 2 (OF 2) ------------------------------------------- 8 DOCUMENTATION The existing documentation (User's Manual, Library Manual) has been comprehensively updated. A new manual, entitled Eiffel Reference Manual, provides a complete reference on Eiffel as a language. This manual is a first draft and is still subject to extension and refinement. Each library comes with an on-line doc directory containing explanations and examples. As noted above, the graphical library and the browsing facilities are delivered with full-fledged example applications in source form. 9 COMPATIBILITY ISSUES Every effort has been made to ensure that previously correct software would still compile and work exactly as before. Minor problems may, however, arise in two areas: reserved words and feature terminology in the data structure library. 2.2 introduces the following new reserved words: Accept BITS define DOUBLE expanded implies indexing infix inspect obsolete prefix unique when xor Any use of these words as identifier in an existing class will raise an error. This will be corrected by choosing other identifiers. In the data structure library new identifiers have been introduced for many library features. All the old ones, however, have been kept as ``obsolete'' features. This fully protects all client classes against any incompatibility; the only practical consequences are compile-time warnings (5.8) that the end of the world is near unless clients mend their ways. To a large extent, descendants are safe too. In some cases, however, classes that used to be correct will produce compile-time errors because they inherit from two library classes with identically named features, raising a name clash. The likelihood of such an event is increased by the unification of feature terminology (2.6), which implies that many library classes have features using a small number of universal names such as put and item. Such problems will be resolved in a straightforward way by adding adequate rename clauses in the descendant classes raising the clashes. The effort involved should be minimal. Programmers who do not want to make this effort immediately may ensure full library compatibility by specifying the ``2.1'' library directory in the UNIVERSE (or SOURCE) entry of the System Description File. This way old software will compile exactly as before. It is of course advisable to migrate as soon as possible to the new libraries. -- -- Bertrand Meyer bertrand@eiffel.com
bertrand@eiffel.UUCP (Bertrand Meyer) (07/14/89)
The following keyword was omitted from the list of new 2.2 reserved words in section 9 of the article ``Release 2.2 Overview'' (second part, <172@eiffel.UUCP>): repeat This is used in the export clause of a class to include an entire parent's export clause, as described in section 5.3 (first part, <171@eiffel.UUCP>). Thanks to Eugene Rodolphe (uunet!ucsd!ames!hc!lanl.gov!cmcl2!esquire!info5!eugene) for pointing out the omission. -- Bertrand Meyer bertrand@eiffel.com