From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-0.3 required=5.0 tests=BAYES_00, REPLYTO_WITHOUT_TO_CC autolearn=no autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,fd96375b28b3103b,start X-Google-Attributes: gid103376,public X-Google-Thread: 1108a1,fd96375b28b3103b,start X-Google-Attributes: gid1108a1,public From: smize@news.imagin.net (Samuel Mize) Subject: "Classes" as packages in Ada Date: 1998/11/24 Message-ID: <73f0p1$4iu8$1@prime.imagin.net> X-Deja-AN: 415257372 Organization: ImagiNet Communications Ltd, Arlington, Texas Reply-To: smize@imagin.net (Samuel Mize) Newsgroups: comp.lang.ada,comp.object Date: 1998-11-24T00:00:00+00:00 List-Id: This message concerns object-oriented design, how that interacts with the Ada language's design philosophy, and some thoughts on additions to Ada that would better support object-oriented programming. I'm cross-posting to comp.object, comp.lang.ada and the Team Ada mailing list, hoping to get some input on this specific suggestion. I've seen good comments between those groups in the past. If you just flat think Ada 95 is bad for object-oriented work, or if you think object-oriented development is nonsense, I hope you'll find another thread in which to express that. Thanks. The particular mechanism I'm proposing is a set of "pragma" (non-executing) statements. These would tell the compiler about the developer's intent for code structures surrounding a tagged type (Ada's object-oriented programming construct). This is explained in detail below. I've tried to state things so that a non-Ada programmer can understand and contribute. - - - - - Ada's tagged types provide (in my opinion) excellent facilities for object-oriented programming. However, some people find it disturbing that Ada doesn't provide a "class" wrapper to encapsulate an object's data type and operations. Each class can be "wrapped" with Ada's "package" -- its encapsulation construct. However, this is not required, and nothing indicates the difference between a class and any other package. I find this to be a partial and unsatisfactory answer. It is not in keeping with Ada's general philosophy of expressing your design intent in the code. Doing so helps others, later, to understand it. It also lets the compiler help you find errors earlier -- for instance, by telling you that a "positive" number has just been assigned a negative value. Note that Ada already provides a tremendous amount of such support for object-oriented development. For example, it isn't possible to send a message to an object that has no method for that message. I believe that Ada provides as much automated checking of an object-oriented design as C++, probably more (a different design philosophy is at work there). However, I want to express some checkable object-oriented semantics that go beyond Ada's current expressive ability. And -- again, because of the Ada 95 "toolbox" philosophy -- we can do so in an orderly way. A tool vendor can define a specific set of semantic limitations, which we can then apply to selected parts of a program. This lets us define how object-oriented programming should be done, and lets the compiler warn us when we fail to follow the paradigm correctly. The mechanism is Ada's "pragma" facility. This lets the programmer tell the compiler something about the program that is outside the code. The general theory is that a pragma shouldn't alter the semantics of a legal program, but it may cause a program to become illegal. For example, there exists pragma Restrictions (...); where one restriction may be that the program has no internal multi-tasking. The compiler can make simplifying assumptions to generate more efficient code. The compiler should reject a program that both states and violates a restriction, although the program would be legal without the pragma. The "Restrictions" pragma is language-defined, but a compiler vendor is allowed to define any other pragmas he wants. (A different compiler may ignore another vendor's pragmas, although it should warn the user if a program contains a pragma that it does not recognize.) I have thought of some pragmas that would let compiler help us enforce our own restrictions when we are doing object-oriented development, with an eye toward catching errors in coding or design. I list them below. My questions to you-all are: 1. Would checking for these semantic items be helpful to catch design or coding errors in an object-oriented design? 2. What other things might we look for? 3. Is there a better technical approach? The specific list of pragmas follows. - - - - - 1. pragma Class; Legal directly inside the declarative region of a package (Ada's module-building construct), plain or generic. The package must export exactly one tagged type. It may only export subprograms in one of these categories: - has one or more parameters of that tagged type - has one or more parameters of the tagged type's class-wide type - follows a Class_Subprograms pragma (see below) This assures the package's user that this is a complete definition of the class, and nothing else. Ideally, the compiler would refuse to allow this type to be extended except in another "Class" package, but I'm not sure if that kind of restriction is possiible. 2. pragma Class_Subprograms; Legal directly inside the declarative region of a "Class" package (plain or generic), after the tagged type has been defined. Subprograms following a "pragma Class_Subprograms" may have no tagged type or class-wide parameters. This allows for selectors and operations on the class itself, such as a function to query how many objects of that class exist, without requiring them to have dummy parameters of the tagged type or its class-wide type. Note that none of the class's methods can follow pragma Class_Subprograms. 3. pragma Inherits; pragma Inherits (subprogram_name...) pragma Inherits_Function (function_name, return_type, parameter_type...) pragma Inherits_Procedure (procedure_name, parameter_type...) pragma In_Fun (function_name, return_type, parameter_type...) pragma In_Pro (procedure_name, parameter_type...) Legal directly inside the declarative region of a "Class" package (plain or generic, although it is unlikely to be used in a generic). With no parameters, "pragma Inherits" states that, for each potentially dispatching (polymorphic) subprograms that this class's tagged type inherits, this class either overrides that subprogram or names it in an "Inherits" or "Hides" pragma. With parameters, "pragma Inherits" must list subprograms that are inherited. Like "pragma Inline," all subprograms of that name are inherited, and multiple "pragma Inherits" may be used. The pragmas Inherits_Function and Inherits_Procedure let you specify the parameter profile of the subprogram being inherited, in case subprograms with the same name are distinguished by their profiles. Pragmas In_Fun and In_Pro are abbreviations for Inherits_Function and Inherits_Procedure, respectively. (They are not intended to suggest that functions are more enjoyable, or that procedures are more remunerative :-) A subprogram listed in an Inherits pragma may not also be over-ridden. All of these pragmas imply the semantics of a plain pragma Inherits. This lets you enumerate, in a given class package, all the methods for that class, even the ones it inherits. It also helps catch spelling or typing errors, where you intend to over-ride a parent class's method but instead create a new message by mistake (e.g., trying to over-ride "Initialize" with a procedure named "Initialise"). 4. pragma Hides (subprogram_name...) pragma Hides_Function (function_name, return_type, parameter_type...) pragma Hides_Procedure (procedure_name, parameter_type...) Legal directly inside the declarative region of a "Class" package (plain or generic, although it is unlikely to be used in a generic). "pragma Hides" must list subprograms that are inherited. Like "pragma Inline," all subprograms of that name are hidden, and multiple "pragma Hides" may be used. A subprogram listed in an Inherits pragma may not also be over-ridden. All of these pragmas imply the semantics of a plain pragma Inherits. If a subprogram listed in a Hides pragma is called, a new exception will be raised. If the compiler detects at run-time that this exception will be raised, the compilation should fail. (This obviously needs some work to get it semantically precise.) This breaks Ada's guarantee that no object can receive a message that it doesn't have a method for, but it does so in an orderly way and in a way that is visible in the package specification. It is preferable, in a few cases, to do so, rather than to inherit an irrational method. "pragma Hides" implicitly over-rides the subprogram's body with one that just raises the exception, and that is what any child of this class inherits. It may un-hide the subprogram by over-riding it. However, it cannot re-inherit the subprogram of the class's parent. 5. pragma Object_First; Legal directly inside the declarative region of a "Class" package (plain or generic). In all parameter lists for the tagged type's subprograms, the controlling tagged-type parameters will come first. If there are none, the class-wide parameters will come first. If the subprogram is a function, "first" may be either the return value, or the first parameter. This enforces a regimen that, like an "object.method" syntax, helps the reader to immediately identify the object that is receiving the message. (Note that we can't really enforce the concept of "recipient" in Ada, but this will at least help the user easily locate the right package.) Ideally, the compiler would refuse to allow this type to be extended except in another "Object_First" package, but I'm not sure if that kind of restriction is possiible. 6. pragma Mixin; Legal directly inside the declarative region of a generic "Class" package. This generic must have exactly one tagged-type input parameter, and extend it as its exported "class" tagged type. This simply clarifies the intent of the programmer. 7. pragma Categorized; pragma Category (Identifier, Subprogram_Identifier...); Legal directly inside the declarative region of a "Class" package (plain or generic). "pragma Category()" must follow the declarations of the subprograms in its parameter list. "pragma Categorized" causes compilation to fail unless all methods of the class (subprograms that precede any "pragma Class_Subprograms") are listed in a "pragma Category". "pragma Category" indicates that the listed subprograms fall into the named category. This is intended to communicate with analysis tools. For instance, a tool may draw a line for each message that objects of one class send to objects of another; all the subprograms in a category could be lumped into one line, so that instead of showing 20 control functions the diagram would show only one line labelled "control". The identifier is arbitrary, and more than one "pragma Category" in a given compilation may use the same identifier. "pragma Category" may be used without "pragma Categorized" to indicate categories for a subset of the class's methods. - - - - - That ends the pragmas I have thought of to date. Best, Sam Mize -- Samuel Mize -- smize@imagin.net (home email) -- Team Ada Fight Spam: see http://www.cauce.org/ \\\ Smert Spamonam