Ada 9X LSN-048-DR THE SYNTAX OF OBJECT-ORIENTED FEATURES 92-September-2 Jean D. Ichbiah INTRODUCTION For as long as I have been involved with Ada 9X (late last year), I have heard some discomfort about the syntax. But the attitude was quite naturally one of taking care of semantic issues first, and to deal with syntactic issues later. This is a defensible approach as long as we are able to realize when the time has come for this "later". However, syntactic issues are very important for the marketing of a language and it would be quite regrettable to have postponed their consideration and find out that it is too late to address them. The esthetic appeal can predispose people favorably and do a lot for its acceptance. Those who underestimate it often speak of "syntactic sugaring" as if it were something unimportant, but many semantically valid languages have failed for lack of charm, for lack of this kind of appeal that rallies supporters without their being able to explain why they like it. So this note - and others, as time permits - is going to concentrate on syntactic issues. Here I will address the syntactic issues of object-oriented features. From a semantic point of view, the semantics built for type extension fit very nicely with the concept of derived types. From a syntactic point of view, there may be room for improvement and for better integration with other features of Ada. SYNTACTIC ISSUES OF OBJECT ORIENTED FEATURES The issues discussed here are the following: * the reserved word "tagged" * the syntax of tagged types * with private * with null THE RESERVED WORD TAGGED Few people are comfortable with the keyword "tagged". The origin of the discomfort is to have a mechanistic keyword designate a high-level abstract concept. Moreover, most other major object-oriented languages call these entities classes (Simula, Eiffel, C++) and the concept is known in the literature as that of class. So we really have no good reason to call it differently in Ada. I am probably not the first one to raise that issue, but it is now time to behave sensibly. Otherwise I can already see the future dialog: C++ Fan: - Ada is not object-oriented, it does not have classes. Ada Fan: - Yes it has classes: they are called "tagged types" C++ Fan: - But this is not quite the same (otherwise they would be called classes) ... Absurd? - No! We have no good reason not to call classes "classes". (There may have been reasons before but they have disappeared as part of the recent simplifications.) By insisting on keeping a different word, we do convey the damaging impression that they are not classes. So why force Ada supporters to be unnecessarily in a defensive position. Why force people with an object-oriented programming background and who learn Ada (after bad experiences with C++, or not having been able to find a Simula or Eiffel compiler) to learn a different terminology for the same concept. Why isolate Ada programmers by forcing a different terminology; why make their access to the wealth of literature and courses on programming methodology more difficult. By maintaining artificial difference, we increase the cost of using Ada. So in what follows I am going to assume that we are using the more intuitive word "class" to reflect the concept of class - as opposed to the more implementation-oriented term of "tag". So we will speak of class types whose objects will be called class objects (just as we have task types whose objects are called task objects, and protected types whose objects are called protected objects). (Note that this does not mean that the explanation of classes and corresponding class objects could not use the notion of tag to indicate that objects of class types have a run-time indication of the actual type, called the tag. The key there is that we dwell on what people know already - classes. We can then introduce the notion of tag to explain how it works.) THE SYNTAX OF CLASSES OR TAGGED TYPES The syntax of class types can be made lighter if the modifier "class" is applied to the type declaration (as opposed to the type definition). Thus we propose: class type ACCOUNT is record ... end record; instead of the current: type ACCOUNT is tagged record ... end record; In particular, the lightness of the new form appears readily for limited private types since we avoid a succession of four boldfaced reserved words. Compare: class type FILE is limited private; to the current syntax: type FILE is tagged limited private; One of the reasons why "class" appears natural and intuitive as a prefix for "type" is that we are already used to having a prefix for "type". For example, we are already used to: task type AGENT is ... and you will soon be very used to the quite natural: protected type SEMAPHORE is ... In addition, as discussed in a separate note, having "class" as a prefix to type has the advantage that class does not need to be a reserved word since no Ada 83 program could have any identifier (other than task) in such a position. In terms of the syntax equation, this proposal can be expressed quite simply as follows: type_declaration ::= full_type_declaration -- as in 83 | incomplete_type_declaration -- as in 83 | private_type_declaration -- as in 83 | class_type_declaration -- NEW class_type_declaration ::= class full_type_declaration -- NEW | class private_type_declaration -- NEW generic_parameter_declaration ::= ... -- as in 83 | private_type_declaration -- as in 83 | class_type_declaration -- NEW | ... -- as in 83 REMOVING WITH PRIVATE The current definition of 9X has a special syntax to explain that a derived type has an extension: package EMERGENCY is type ALERT is new SYSTEM.ALERT with private; ... private type ALERT is new SYSTEM.ALERT with record ... end record; end; The "with private" clause is in a way a violation of privacy: why would an outside user of the type have to know that there are additional components introduced in the private part? The simpler following alternative is preferable: package EMERGENCY is type ALERT is new SYSTEM.ALERT; -- (1) ... private type ALERT is new SYSTEM.ALERT with -- (2) record ... end record; end; At (1) we see EMERGENCY.ALERT defined as a type derived from SYSTEM.ALERT. At (2) we find that there is an extension. It is very proper for a private part to introduce additional private information so that this should be no surprise. The logic behind this is as follows: * By declaring the ancestor type to be a class type, we forewarn users to expect extensions * Therefore these extensions may come as "with record ..." whether in a visible part or in a private part * And so, there is no need for a syntactic marker (such as "with private") to indicate that it is normal for such a type to receive an extension: the fact that it is a subclass is sufficient. Another advantage of this alternative is that it removes the complexity of having to distinguish between: type B is new A with private; type B is new A with limited private; which is quite obscure from the point of view of the reader. MORE IMPROVEMENT: SUBCLASSES ARE ALSO CLASSES We can get even more uniformity by realizing that subclasses are also classes and therefore we should require the prefix "class" for all class type declarations, whether they declare the root of a class or a subclass. (In fact, that was already a consequence of the syntax given earlier.) So the examples become: package EMERGENCY is class type ALERT is new SYSTEM.ALERT; -- (1) ... private class type ALERT is new SYSTEM.ALERT with -- (2) record ... end record; end; and for the A example: class type A is new B; Note that this achieves a nice property for readability: every class type start by the syntactic marker "class". The MRT has seen the value of having an easy identification of these types (see 4.6 page 3- 4, last paragraph): "[Note: By requiring "with null" we ensure that all tagged types include either the reserved word "tagged" or "with" in their declaration [...] This should enhance readability ..."" And the solution proposed here goes one step further, making this identification even simpler - always with the single keyword "class" and as the first lexical unit of the type declaration. REMOVING WITH NULL Having removed successfully "with private", a similar treatment can be applied to "with null". There were two motivations for "with null". The first was to be able to recognize easily a subclass declaration, and, as we discussed before, this is better achieved by the syntactic marker "class" in the revised syntax. The second had to do with allowing normal type conversions (as opposed to extension aggregates) between a parent class type and a derived subclass. For example: type VEHICLE is tagged record WEIGHT : KILOS; end record; extended with: type OTHER_VEHICLE is new VEHICLE with null; Given the above, a conversion would be written as follows V : VEHICLE := ...; W : OTHER_VEHICLE := (V with null); where the "with null" emphasizes that we are extending V with nothing. But we can do better by removing "with null" altogether and rewrite the above example (using some other simplifications for the syntax of extension aggregates - see reference 92-1339.a JDI 92-9-1) as follows: class type VEHICLE is record WEIGHT : KILOS; end record; class type OTHER_VEHICLE is new VEHICLE; V : VEHICLE := ...; W : OTHER_VEHICLE := (VEHICLE'PART => V) We very much agree with the MRT when they say in the already quoted paragraph (see 4.6 page 3-4, last sentence): " ... we expect null extensions to be less common than record extensions." Consequently, it is good economy of concept not to have a special feature for a rare situation and accept what you already get from other features. This is what we have done here: the aggregate with a single component has the general syntax of one-component aggregates. SUMMARY AND CONCLUSIONS As I said in introduction, now is our last chance for polishing the syntax and wrap the new semantics in a syntax that harmoniously reflects it. In this sense, the above proposals achieve improvement and should help provide a good image of Ada 9X classes. To summarize: * The syntax uses the keyword "class" as prefix of class and subclass declarations. * Consequently, all class and subclass declarations being marked by this keyword, there is no more need for the forms "with private" and "with null", and they can be removed from the language definition. Finally, calling classes just classes will help building the perception that Ada 9X is in the mid-stream of object-oriented programming. APPENDIX I: A SYNTACTIC VIEW OF THE ACHIEVED SIMPLIFICATION One measure of the simplification achieved is the comparison of the syntactic rules for describing the alternatives. For 4.6 the relevant equations are as follows (an ellipsis is used for something that is as in Ada 83): VERSION 4.6 RULES: derived_type_definition ::= new subtype_indication [type_extension_part] type_extension_part ::= with record component_list end record | with private | with null aggregate ::= ... | extension_aggregate extension_aggregate ::= (expression with record_aggregate) | (expression with null) generic_parameter_declaration ::= ... | type identifier [formal_discriminant_part] is [tagged] [limited] private; | type identifier [formal_discriminant_part] is new type_mark [with private] | ... With the proposed revisions the syntax would be as follows: PROPOSED RULES: type_declaration ::= ... | class_type_declaration class_type_declaration ::= class full_type_declaration | class private_type_declaration type_definition ::= ... | extended_type_definition extended_type_definition ::= derived_type_definition with record type definition generic_parameter_declaration ::= ... | class_type_declaration | ... It is not so much the fact that the proposed syntax uses 6 rules instead of 9 that is a simplification, it is the fact that it does not require duplications and therefore should be easier to understand. See for example the reuse of the notion of class_type_declaration in a generic parameter declaration, and also the reuse of record_type_definition in the extended_type_definition. Reuse is important because it means less to learn. See also, how the use of class_type_declaration in a type declaration and in a generic parameter declaration nicely parallels that of private_type_declaration. APPENDIX II: AN EXAMPLE To get a more global feel for the proposed syntactic simplifications, here is a rewriting of the example given in RRAT 4.2.3.5 (August 1991): package P1 is class type PARENT is private; private class type PARENT is record null; end record; end P1; with P1; package P2 is class type VISIBLE_EXTENSION is new P1.PARENT with record VISIBLE_PART : DATA_TYPES; end record; end P2; with P1; package P3 is class type PRIVATE_EXTENSION is new P1.PARENT; private class type PRIVATE_EXTENSION is record MORE : MORE_DATA_TYPES; end record; end P3; package P3.CHILD is class type ANOTHER_EXTENSION is new PRIVATE_EXTENSION; private class type ANOTHER_EXTENSION is new PRIVATE_EXTENSION with record YET_MORE : YET_MORE_DATA_TYPES; end record; end P3.CHILD;