bertrand@eiffel.UUCP (Bertrand Meyer) (01/16/90)
A change planned for the object creation in Eiffel -------------------------------------------------- In the tradition of glasnost' that has marked the evolution of Eiffel, this posting and one or two subsequent ones will describe the changes planned for version 3 of the language. These are cleanup changes and do not affect anything fundamental. ---------------------------------------------------------------------- |WARNING: The change described here is planned for version 3 of the | |environment, not to be released until late 1990. | | |Any change in the language supported by Interactive's tools | |will be accompanied by CONVERSION TOOLS to translate ``old'' syntax | |into new. Programmers will NOT need to perform any significant work | |to update existing Eiffel software. | | | |This posting is made solely for the purpose of informing the Eiffel | |community about ongoing developments. Although the posting has been | |preceded by careful reflection and internal discussions within | |Interactive, we make no commitment at this point that the features | |described here will actually be included, and, if they are, that | |their final form will be the exact one shown below. | ---------------------------------------------------------------------- Purpose of the change. ---------------------- The purpose of the change is to solve several problems at once through an improved syntax and semantics for creating objects in Eiffel. None of the problems is really major since, as indicated below, each has a reasonable solution in the current language. Some of them are small practical nuisances; others are irregularities which impair the elegance of the overall language design. Problem 1: In current Eiffel, there is only one creation procedure per class. To have more than one creation mechanism, you must use proper descendants (e.g. POLAR_POINT, CARTESIAN_POINT inheriting from POINT). Problem 2: By default, the Create procedure is not inherited. To reuse the same creation mechanism in a proper descendant, you must rely on renaming. Problem 3: There is no syntactical facility directly supporting the creation of an instance of a proper descendant. If a1 is of type A and you want to create it as an instance of B, a proper descendant of A, you must use an intermediate entity of type B. Problem 4: Normal feature applications a.f (...) act on the object associated with reference a, and cannot change that reference itself. There are, however, three exceptions: Create, Forget and Clone. Such irregularity is unpleasant. Problem 5: The ``Create'' procedure of a class has a somewhat special status. In particular, the current edition of ``Eiffel: The Language'' does not say clearly what happens if it is called unqualified by another procedure of the class. Problem 6: There is an inconsistency between the status of Clone, a creation instruction (``Clone'' itself is a keyword) and the status of copy, deep_copy, deep_clone, all of which are routines of the universal class ANY. Conceptually, however, Clone does not appear fundamentally different. Furthermore, why the instruction is written x.Clone (y) rather than x := y.clone (with ``clone'' a function) is not clear; this has been pointed out by many people. The language change ------------------- There is no more special role for a ``Create'' procedure. Instead, a class may contain a clause creation f, g, h, ... where the names listed are those of procedures of the class. These are the ones that may be used for creating instances of the class. ``Create'' ceases to be a keyword; ``creation'' becomes one. (The risk of clash with identifiers in existing classes appears minimal.) Procedures appearing in the creation list are otherwise normal. They may or may not appear in the export list. They may be introduced in the class itself or be obtained from a proper ancestor. A procedure which appears in the creation clause of a class may or may not appear in the creation clause of its proper descendants. As with the export clause, the creation clause may contain an item of the form repeat A where A is a proper ancestor; this is equivalent to a list of the creation procedures of A. There is only one creation instruction. Its most general form is x !D! f (...) where x is a read-write entity of some type C and D is a descendant of C; f must be a creation procedure of D. This creates an instance of D, applies the default initializations, calls f with the actual arguments given, and attaches the resulting object to x. D may be omitted in the most frequent case, for which it is the same as C, giving the form x !! f (...) For a class which has no creation procedure (meaning that the default initializations are sufficient to ensure the invariant), the syntax becomes simply x !! Because a creation procedure is a normal procedure, it may be used as the target of a feature call, either local or (if the procedure is exported) remote. For example, if a procedure set_polar (ro, theta: REAL) is listed in both the ``export'' and ``create'' clauses of a class, then for p of type POINT both of the following are correct, with a different semantics: p !! set_polar (1, pi/2) -- Create point with magnitude and angle given. p.set_polar (1, pi/2) -- Change point (previously created) to -- magnitude and angle given. Cloning ------- As suggested above, cloning ceases to be an instruction; a function clone: like Current is now part of class ANY. Forget will also disappear as an instruction. This is tied with another small change (having to do with ``void'' values) and will be described in another message. A note on conversion of existing software ----------------------------------------- Conversion of existing Eiffel classes will be straightforward. For a class with a Create procedure, it suffices to add a clause creation create Since ``create'' is no longer a keyword, nothing else changes. Calls of the form a.Create (...) will be replaced by a!!create (...) -- -- Bertrand Meyer bertrand@eiffel.com
keithc@cognos.UUCP (Keith Campbell) (01/18/90)
In article <226@eiffel.UUCP> bertrand@eiffel.UUCP (Bertrand Meyer) writes: [about plans for version 3] >There is only one creation instruction. Its most general form is > x !D!f(...) >where x is a read-write entity of some type C and D is a descendant of >C; f must be a creation procedure of D. This creates an instance of >D, applies the default initializations, calls f with the actual arguments >given, and attaches the resulting object to x. >D may be omitted in the most frequent case, for which it is the same >as C, giving the form > x !!f(...) >For a class which has no creation procedure (meaning that the default >initializations are sufficient to ensure the invariant), the syntax >becomes simply > x !! In the interest of further reducing useless local variables, I suggest that you go one step further and allow creation of anonymous objects. Thus x !D!f(...) would be equivalent to x := !D!f(...) The improvement is that now we can pass newly created objects as actual parameters without the need for the declaration of local variables: foo( !D!f(...), !E!g(...), ... ); If the compiler is given the ability to infer the (most general) class of object which needs to be created, then x := !!f(...) would be equivalent to x !!f(...) and x := !! would be equivalent to x !! All of us are comfortable with the use of the symbol "1" to mean !Integer!.incremented Why not extend this flexibility to all classes? Note that "incremented" cannot be confused with a creation feature because it appears following the dot ".". In this case, the compiler must generate code to first create this object (using the default initializations) before the "incremented" function may be applied. -- 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
sommar@enea.se (Erland Sommarskog) (01/21/90)
Bertrand Meyer (bertrand@eiffel.UUCP) writes: >There is only one creation instruction. Its most general form is > > x !D! f (...) > >where x is a read-write entity of some type C and D is a descendant of >C; f must be a creation procedure of D. This creates an instance of >D, applies the default initializations, calls f with the actual arguments >given, and attaches the resulting object to x. While I have nothing against the concept as such, rather the opposite, I am not appealed of the syntax. I have the opinion that artificial symbols should be avoided, particulary if they are not commonly used elsewhere. If you don't know Eiffel and looks at a piece of code and sees "x !!" it will take some time until you understand what is going on. (It is possible that !! is common in this meaning. It is new to me, however.) Maybe I have had a too big exposure to Cobol, but I would propose create x as D using f(...) with "as D" and "using f(...)" being optional as in Dr. Meyer's article. This would require a new keyword "using" and that "create" would remain being one. Whether "using" is a common identifier, I don't know, but I wouldn't expect it to be. This not only has the advantage to allievate the readability, but also it gives a better understanding of what you are doing. -- Erland Sommarskog - ENEA Data, Stockholm - sommar@enea.se Unix is a virus.
bertrand@eiffel.UUCP (Bertrand Meyer) (01/21/90)
In <7862@cognos.UUCP>, keithc@cognos.UUCP (Keith Campbell) suggests using creation expressions rather than creation instructions. For example the instructions [1] > x := !D!f(...) [2] x := !D! would assign to x the value of the right-hand side expressions, denoting a newly created object of type D, (after initializing this object through f in the case [1]). The advantage is that such an expression can also be used in contexts other than assignments, for example as actual argument to a routine. In keeping with my earlier note, Mr. Campbell would also accept one of [3] > x := !! [4] x := !!f (...) if the base class of x's type has no creation procedure. Forms [3] and [4], however, do not seem acceptable. The right-hand side is not a true expression since its meaning depends on the type of x, which does not even occur in that right-hand side. As Hoare pointed out in his ``Hints on Programming Language Design'' Stanford CS Report, 1973, reprinted in Ellis Horowitz's ``Programming Languages: A Grand Tour'', Computer Science Press, 1983), one of the pleasant properties of expressions is that an expression such as E + F can be understood solely in terms of its parts. Here this is clearly violated. Mr. Campbell's major argument, when applied to this case, provides further evidence of the same problem: obviously, forms [3] and [4] cannot be used as anonymous actual arguments, since by themselves the expressions are ambiguous (you cannot understand them without further information on their intended type). If we want to support anonymous creation expressions, then, the only solution is to accept forms [1] and [2] only: in other words, all creation operations must list the class. This comes very close to the Simula 67 convention with a different syntax: in Simula, object creation is obtained by writing expressions (not instructions) of the form new class_name (arguments) where the class_name is required. I considered this at the time of the original Eiffel design but rejected it for several reasons, including convenience. Most creation operations (perhaps 90% in my experience) are of form [3] or [4] and it seems to be a nuisance to require the programmer to repeat the type of x in every case. It is interesting to relate this to another suggestion that I just received through a private mail message. The author of that message suggested that the initializing routine should be required; in other words, forms [2] and [3] would not be supported. This is easy to achieve: we could just have a procedure ``nothing'' (which does what its name indicates) in class ANY. Then instead of an empty creation clause you would be required to write a clause of the form creation nothing if you want the class to have instances. But then if we combine this suggestion with what the above discussion indicates as the result of Mr. Campbell's suggestion, we have to write [5] x := !CLASS_NAME!nothing instead of just [6] x !! For creation instructions, a quite common occurrence in Eiffel software (at least for basic software components, such as libraries), I frankly prefer [6] to [5] and I think most programmers will agree. -- -- Bertrand Meyer bertrand@eiffel.com
chl@cs.man.ac.uk (Charles Lindsey) (01/23/90)
bertrand@eiffel.UUCP (Bertrand Meyer) writes: >In keeping with my earlier note, Mr. Campbell would also accept one of >[3] > x := !! >[4] x := !!f (...) >if the base class of x's type has no creation procedure. >Forms [3] and [4], however, do not seem acceptable. The right-hand side >is not a true expression since its meaning depends >on the type of x, which does not even occur in that right-hand >side. As Hoare pointed out in his ``Hints on Programming Language >Design'' Stanford CS Report, 1973, reprinted in Ellis Horowitz's >``Programming Languages: A Grand Tour'', Computer Science Press, 1983), >one of the pleasant properties of expressions is that an >expression such as E + F can be understood solely in terms >of its parts. Here this is clearly violated. Although Tony Hoare's principle enunciated above seems a fundamental part of strong typing, there are precedents for breaking it in special very specific circumstances (where the meaning of the exception is "obviously" OK). Thus in PASCAL (also ALGOL 68) one may write somepointervariable := nil; even though the type of nil can only be determined from its context. Also, in ALGOL 68 one could write somevariable := SKIP; #SKIP returns an undefined value# and even somevariable := GOTO idontcare; #to break off the computation entirely# In the case if Eiffel, I should have thought that the same exception could be made for x := !!. It is not so different from x := null which apparently Bertrand is prepared to condone.
bertrand@eiffel.UUCP (Bertrand Meyer) (02/17/90)
From <607@m1.cs.man.ac.uk> by chl@cs.man.ac.uk (Charles Lindsey): > > Although Tony Hoare's principle [i.e., the meaning of an expression > should be fully deducible from the meanings of its constituents] > seems a fundamental part of strong typing, there are precedents for > breaking it. [..] Thus in PASCAL (also ALGOL 68) one may write > > somepointervariable := nil; > > even though the type of nil can only be determined from its context [...] > > In the case if Eiffel, I should have thought that the same exception could be > made for x := !!. It is not so different from x := null which apparently > Bertrand is prepared to condone. Actually it is different because in Pascal the type status of `nil' is murky: nil is a constant which must belong to every type, which is contradictory with the rest of the type system. No such problem arises in Eiffel thanks to the introduction of NONE: because NONE is a descendant of every class, we can define the type of `null' as being NONE, so that the assignment x := null is always typewise valid regardless of the type of x. -- -- Bertrand Meyer bertrand@eiffel.com