bertrand@eiffel.UUCP (Bertrand Meyer) (03/19/89)
Version 2.2 of Eiffel includes two new facilities for multi-branch choices. The following is extracted from an internal document describing the purpose and form of these extensions and (under suitable form) will be found in the documentation for the new release. It seemed that readers of this news group would be interested in seeing an advance version. ------------------- CUT HERE ---------------------------------- MULTI-BRANCH CHOICES IN EIFFEL Bertrand Meyer February 21, 1989 (Edited for posting to Usenet, March 18) 1 Overview ---------- The introduction of a ``test'' instruction and of a ``unique'' form of constant attribute declaration is meant to enable programmers to write simple multi-branch choices. The extension is purposely simple and limited. Advanced cases of multi-branch discriminations should be handled not by a test instruction (the equivalent of Pascal's or Ada's case instruction) but by using redefined routines and dynamic binding. Dynamic binding ensures openness by making it possible to add new cases at any time without disrupting the structure of existing software. In contrast, test or case instructions are closed: they specify a fixed set of choices. They may be needed, however, in simple cases of choosing between a set of fixed alternatives. For this reason, there was no form of case instruction in Eiffel until version 2.1. Although it came as a shock to some users, this absence was not a major problem in actual use of Eiffel once the inheritance techniques had been understood. One user remarked that ``not having a case instruction for six months was excellent for new object-oriented programmers''. Once the concepts have been understood, however, there remain situations in which a simple multi-branch instruction is needed. These include straightforward discriminations between a fixed set of cases, where the context does not justify introducing one heir class per case, and discriminating between ASCII characters on input. The design reported here and implemented for version 2.2 of Eiffel addresses these cases. The resulting constructs should be used with reason; great care should be taken not to destroy the openness of object-oriented structures, which is such a key factor in the extendibility and the reusability obtained through proper application of the object-oriented method. In designing these constructs, it was strongly felt that the extension should remain modest, and should not introduce any fundamentally new concept. In particular, no new data type is involved; no ``literal'' type is needed in Eiffel. The extension involves two simple concepts: + A new form of constant value, written unique, making it possible to define constant attributes whose values are chosen by the compiler, not by the programmer. + A new control structure, test... end, similar to Pascal's case instruction. 2 Multi-branch choices in current Eiffel ---------------------------------------- In Eiffel version 2.1, as described in the book ``Object-Oriented Software Construction'', a multi-branch choice can be implemented as an if... then... else... instruction: if e = v1 then i1 elsif e = v2 then i2 ... elsif e = vn then in This works but has two disadvantages: + Performance: when the values v1, v2 etc. are consecutive integer or character values, the implementation cannot easily take advantage of the well- known constant-time ``jump table'' technique; instead, tests must be performed sequentially. + Convenience: It is often the case that the values v1, v2 are symbolic constants, whose value is not meaningful as long as all values are different. This is handled in Pascal or Ada by defining these values as leterals. In current Eiffel, the constants must be declared individually and their values chosen explicitly by the programmer, as in blue: INTEGER is 1; white: INTEGER is 2; red: INTEGER is 3; ... This is tedious. The design described below removes these limitations. This is its sole aim; it should be viewed as a mechanism to improve the performance and convenience of the current Eiffel techniques, NOT AS A NEW TECHNIQUE. No conceptually new technique is needed here, only existing techniques made simpler to use. 3 ``Unique'' constants ---------------------- The value of a constant integer attribute may now be declared not just as a literal value (such as, say, 347) but as ``unique'' (``unique'' is a new keyword). For example: blue: INTEGER is unique; white: INTEGER is unique; red: INTEGER is unique; A ``unique'' value is chosen by the compiler; all attributes declared as ``unique'' within a class are uaranteed to have different values, hence the name. No further property is guaranteed to hold of these values; programmers have no control over the policy used by the compiler to assign ``unique'' values. Uniqueness of values is guaranteed within each class; ``unique'' entities declared in different classes, whether or not they are related by inheritance, are not guaranteed to have different values. Only integer constants may be declared as ``unique''. As a notational facility, two or more ``unique'' declarations may be grouped, as in blue, white, red: INTEGER is unique; The meaning of such a combined declaration is the same as if the declarations had been written separately as above. 4 The ``test'' instruction -------------------------- The test instruction is written as follows: test e when v11, v12, ... then instructions when v21, v22, ... then instructions ... when vn1, vn2, ... then instructions end In this notation: e is an expression of type INTEGER or CHARACTER; v11, v21, ..., v21, etc., called ``test constants'' in the sequel, are constants of the same type as e; instructions represents a sequence of zero or more instruction separated by semicolons. The test constants may be explicit constant values or constant attributes; in the latter case they may have been declared as ``unique''. The following rules apply to the set of test constants: + If any of these constants are declared as ``unique'', then all of them must be so declared; furthermore, they must have all been declared IN THE SAME CLASS. (This means REALLY in the same class: in particular, it is not permitted to have some of them declared in a class and some coming from proper ancestors of that class.) + If the test constants are not declared as ``unique'', that is to say if they have explicit values, then all of these values must be different. For test 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 one of the following: 3..5 'a'..'d' The effect of a test instruction of the above form is defined as follows. The value of e is computed. By the above rules, there can be at most one ``when'' branch such that one of its test values (after expansion of any intervals) is equal to the value of e. If this is the case, the corresponding ``then'' clause is executed and this terminates the execution of the test instruction. If no test value is equal to the value of e, an exception is raised. The symbolic name for the corresponding exception code in the library class EXCEPTIONS is Invalid_test_value. It is important to note that the test instruction has no ``else'' clause or equivalent, and that its effect if none of the test values matches that of the expression is NOT to execute an empty operation but to trigger an exception. The test instruction (as its Pascal and Ada counterparts) is potentially dangerous because it reflects the programmer's expectation that the value of e will be one of the stated test values. The least we can expect from the programmer is that he precisely specify what these expected values are. If at run-time the expression does not evaluate to any of these, then this is a serious abnormal case that should be detected immediately, not quietly ignored.
sommar@enea.se (Erland Sommarskog) (03/19/89)
Bertrand Meyer (bertrand@eiffel.UUCP) writes: > It is important to note that the test instruction has >no ``else'' clause or equivalent, and that its effect if none of >the test values matches that of the expression is NOT to >execute an empty operation but to trigger an exception. The >test instruction (as its Pascal and Ada counterparts) is >potentially dangerous because it reflects the programmer's >expectation that the value of e will be one of the stated >test values. The least we can expect from the programmer is >that he precisely specify what these expected values are. >If at run-time the expression does not evaluate to any of >these, then this is a serious abnormal case that should be >detected immediately, not quietly ignored. I fully share the view that if the case selector is not matched, this should be considered as an error and an exception should be raised. However, I object to the omission of an OTHERWISE clause and to to the comparison to Ada and Pascal. Ada and Pascal first. I don't have any Pascal standard reference at hand, but the behaviour I expect is to abandon execution if the CASE selector is out of range, unless an OTHERWISE clause have been given. (OTHERWISE is not standard Pascal.) A problem may be if the compiler allows you to turn these checks off. (VAX- Pascal does.) Ada have a different approach. The fragment below is illegal Ada: ch : character; ... Case ch of when 'A'..'Z' => Text_io.Put_line('Capital letter'); when 'a'..'z' => Text_io.Put_line('Common letter'); End case; Ada requires you to either mention all elements of the selector's subtype or give an OTHERS clause. The only possibility to get into a CASE statement in Ada without a match is if the selector is outside its subtype due to defaulting from initiating it. This certainly is a problem, but hardly not one with CASE statements as such. Why have an OTHERWISE clause? Say we have a simple interactive program where we read one character from the user and then chose a command. Without OTHERWISE I would have to write: (Pascal syntax, since I'm lazy.) CASE 'ch' OF 'A', 'a' : Do_a_command; 'F', 'f' : Do_f_command; 'R', 'r' : Do_r_command; chr(0)..'@', 'B'..'E', 'G'..'Q', 'S'..'`', 'b'..'e', 'g'..'q', 's'..chr(255) : writeln('Illgeal command'); END; With an OTHERWISE it would be very cleaner. Not talking on how much easier it would be to add one more command. There are other situations where an OTHERWISE nicely exhibits the problem behind the logic. At least in Pascal and Ada programs. I understand that Eiffel will take away some of these situations, but I doubt that it will remove all of them. My suggestion is that the TEST statement is given an optoinal OTHERWISE clause. If the clause is not present, an exception is raised if there is no choice matching the selector. -- Erland Sommarskog ENEA Data, Stockholm This signature is not to be quoted. sommar@enea.se
bertrand@eiffel.UUCP (Bertrand Meyer) (03/23/89)
From <4377@enea.se>, sommar@enea.se (Erland Sommarskog): > I object to [...] the comparison to Ada and Pascal. > [... Discussion of Pascal and Ada mechanism ..] I checked my original message and I don't think anything it said contradicted Mr. Sommarskog's discussion of the Pascal and Ada mechanisms. My only precise reference to these languages in the description of the multi-branch choice <122@eiffel.UUCP> was: >> The test instruction (as its Pascal and Ada counterparts) is >> potentially dangerous because it reflects the programmer's >> expectation that the value of e will be one of the stated test values. I think this statement is correct; in particular, the Pascal and Ada techniques described by Mr. Sommarskog in his message do not invalidate it. I also considered in detail Mr. Sommarskog's arguments in favor of an optional ``else'' clause and I think he is right. The mechanism will be emended accordingly. An exception will still be raised if no ``else'' clause is present and the value of the discriminant matches none of the values listed in the ``when'' clause. There will also be one minor other change: the keyword for the multi-branch instruction will be something else than ``test''. It occurred to me that at least one out of every three new users is bound to write a first trial class called TEST, implying that his first encounter with the Eiffel tools would now be some message of the form ``test is a reserved word and cannot be used as a class name''. Better choose a less obvious keyword. I am grateful to Mr. Sommarskog for his observations. -- -- Bertrand Meyer bertrand@eiffel.com
day@grand.UUCP (Dave Yost) (03/24/89)
In article <130@eiffel.UUCP> bertrand@eiffel.UUCP (Bertrand Meyer) writes: > I also considered in detail Mr. Sommarskog's arguments in favor of an >optional ``else'' clause and I think he is right. The mechanism will >be emended accordingly. An exception will still be raised if no ``else'' My hat off to you for being so available, responsive, candid, and open to suggestion in this public forum, as in this random example. Very refreshing. May you not become *so* swamped with customers that you can't keep it up. --dave yost
db@lfcs.ed.ac.uk (Dave Berry) (03/25/89)
In article <122@eiffel.UUCP> bertrand@eiffel.UUCP (Bertrand Meyer) writes: > > For test 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 one of the following: > > 3..5 > > 'a'..'d' Would it not be possible to extend this notational fcility to unique constants declared in the same declararion? To use Bertrand's example: > blue, white, red: INTEGER is unique; If this notation were taken to mean that the unique values given to blue, white and red must be consecutive, tests could be made against the ranges blue..white, white..red and blue..red. In a small example like this, it isn't too useful, but it might be useful with a larger case. Alternatively, since all unique constants in a test must be defined in the same class, one could define all unique constants defined in the same class to be consecutive in the order in which they were defined. Then the above declaration would just be an abbreviation, as before. A further note on else clauses: If you have user-controlled exceptions, one can use an else clause to raise an exception suitable to the particular test statement which fails to match. This can be more useful than raising a system exception. I know Bertrand has accepted Erland's arguments for an else clause; but I think this use for them is worth mentioning anyway. (The references I have on eiffel are strongly against exceptions, so I presume they're out of date! I'm currently trying to get hold of the eiffel book; I apologise if I've made any inaccurate assumptions in the above.) Dave Berry, Laboratory for Foundations of Computer Science, Edinburgh. db%lfcs.ed.ac.uk@nss.cs.ucl.ac.uk <Atlantic Ocean>!mcvax!ukc!lfcs!db
sommar@enea.se (Erland Sommarskog) (03/25/89)
Bertrand Meyer (bertrand@eiffel.UUCP) writes: >I said: >> I object to [...] the comparison to Ada and Pascal. > > I checked my original message and I don't think anything it said >contradicted Mr. Sommarskog's discussion of the Pascal and >Ada mechanisms. Yes, that's perfectly correct. I misread Bertrand Meyer's article to say something it didn't say. My excuses for the confusion. -- Erland Sommarskog ENEA Data, Stockholm This signature is not to be quoted. sommar@enea.se
bertrand@eiffel.UUCP (Bertrand Meyer) (03/26/89)
From <1638@etive.ed.ac.uk>, db@lfcs.ed.ac.uk (Dave Berry): > Would it not be possible to extend the notational facility > [for intervals, e.g. 3..5] > to constants declared in the same declaration? [If] > > blue, white, red: INTEGER is unique; > > were taken to mean that the unique values given to > blue, white and red must be consecutive, tests could be made against > the ranges blue..white, white..red and blue..red. The above is defined formally as a syntactical abbreviation for blue: INTEGER is unique; white: INTEGER is unique; red: INTEGER is unique In Eiffel, the order of declarations of features within a class is immaterial. I would hesitate to make an exception to this rule. I have always been uneasy with the ordering that exists between the values of an enumeration type in Pascal, and in particular with the predefined ``ord'' function. I believe it is preferable to think of enumerated values in Pascal, or ``unique'' values in Eiffel, as elements of a set rather than of a sequence. This is largely a matter of taste. I am not claiming that there is just one correct answer. > The references I have on Eiffel are strongly against exceptions, > so I presume they're out of date! They must be quite old indeed. I was so shocked by the Ada exception mechanism that for a while I was dead set against exceptions. After looking more carefully at assertions, however, I realized that there is a need for a disciplined exception mechanism, based on program correctness techniques and on an analysis of what ``abnormal case'' means from a theoretical viewpoint. This mechanism was incorporated into Eiffel. In my biased view it is one of Eiffel's main contributions to software engineering. The methodological rationale and the mechanism itself are described in detail in an article entitled ``Programming as Contracting'', which has been sitting for about two centuries in the refereeing process of a journal. Various bits of the article found their way into section 7.10 of ``Object-Oriented Software Construction'', into a recent paper in Springer-Verlag's ``Structured Programming'' journal (Vol. 10, no. 1, pp. 19-39), and into the next installment of the Eiffel column in the Journal of Object-Oriented Programming, due to appear (I think) in May. -- -- Bertrand Meyer bertrand@eiffel.com