!standard 3.4(01) 05-11-30 AI95-00401/07 !standard 3.4(03) !standard 3.4(05) !standard 3.4(08) !standard 3.4(17) !standard 3.4(18) !standard 3.4(22) !standard 3.4(23) !standard 3.4(23.1) !standard 3.4(27) !standard 3.4(35) !standard 3.4.1(02) !standard 7.3(16) !standard 7.3(20) !standard 9.1(9.1) !standard 9.4(11) !standard 12.5.1(05) !standard 12.5.1(15) !standard 12.5.1(21) !standard 12.5.5(04) !class amendment 05-01-25 !status Amendment 200Y 05-03-01 !status ARG Approved 7-0-3 05-02-13 !status work item 05-01-25 !status received 05-01-25 !priority High !difficulty Medium !subject Terminology for interfaces !summary (See proposal.) !problem The current wording calls "interface ancestor" an interface that is mentioned in an interface_list. This terminology is used in the description of derived types, interface types, private extensions, task and protected objects and types, and formal derived types. It causes some confusion because the term "ancestor" is already a technical term in Ada 95, with a subtly different meaning. For example, a type is an ancestor of itself, but the "interface ancestor" relationship doesn't have that same property. In some cases where the word "ancestor" is used alone, it is unclear whether it is a shorthand for "interface ancestor" or a bona fide usage of the technical term. Also, the description of private extensions and formal private types makes use of the phrase "ancestor subtype" in context where interfaces also play a role, creating even more obscurity. This terminology issue was briefly discussed in the context of AI 251, but at the time no good alternative was proposed, and it's only when reading the integrated manual that the ambiguities become obvious. !proposal We propose to call "progenitor" an interface mentioned in an interface_list. We then define "ancestor" to mean "parent or progenitor, any level". Most of the rules that use the words "parent" or "ancestor" remain valid with this definition, although in a few cases we need to say "parent or progenitor". While the wording changes are numerous, they are mostly limited to 3.4 and 12.5.1. Note that not all changes are terminology changes; some are just plugging holes. For instance, there are a number of places where the wording say "parent" for rules that should also apply to interfaces. In other cases the new terminology fixes wording that was otherwise wrong or incomprehensible, depending on how the word "ancestor" was interpreted. !wording Change 3.4(1) to read: A derived_type_definition defines a new type (and its first subtype) whose characteristics are derived from those of a parent type{, and possibly from progenitor types}. Change 3.4(3) to read: An interface_subtype_mark in an interface_list names a *progenitor subtype*; its type is the *progenitor type*. The parent_subtype_indication defines the *parent subtype*; its type is the *parent type*. A derived type has one parent type and zero or more {progenitor} types. Change 3.4(5) to read: If there is a record_extension_part, the derived type is called a record extension of the parent type. A record_extension_part shall be provided if and only if the parent type is a tagged type. An interface_list shall be provided only if the parent type is a tagged type. Change 3.4(8) to read: Each class of types that includes the parent type {or a progenitor type} also includes the derived type. Change 3.4(17) to read: For each user-defined primitive subprogram (other than a user-defined equality operator -- see below) of the parent type {or of a progenitor type} that already exists at the place of the derived_type_definition, there exists a corresponding inherited primitive subprogram of the derived type with the same defining name. Primitive user-defined equality operators of the parent type {and any progenitor types} are also inherited by the derived type, except when the derived type is a nonlimited record extension, and the inherited operator would have a profile that is type conformant with the profile of the corresponding predefined equality operator; in this case, the user-defined equality operator is not inherited, but is rather incorporated into the implementation of the predefined equality operator of the record extension (see 4.5.2). Change 3.4(18) to read: The profile of an inherited subprogram (including an inherited enumeration literal) is obtained from the profile of the corresponding (user-defined) primitive subprogram of the parent {or progenitor} type, after systematic replacement of each subtype of its profile (see 6.1) that is of the parent {or progenitor} type with a corresponding subtype of the derived type. For a given subtype of the parent {or progenitor} type, the corresponding subtype of the derived type is defined as follows: Change 3.4(22) to read: The same formal parameters have default_expressions in the profile of the inherited subprogram. Any type mismatch due to the systematic replacement of the parent {or progenitor} type by the derived type is handled as part of the normal type conversion associated with parameter passing -- see 6.4.1. Change 3.4(23) to read: If a primitive subprogram of the parent {or progenitor} type is visible at the place of the derived_type_definition, then the corresponding inherited subprogram is implicitly declared immediately after the derived_type_definition. Otherwise, the inherited subprogram is implicitly declared later or not at all, as explained in 7.3.1. Delete the paragraph added after 3.4(23) by AI95-00251. Change 3.4(27) to read (this includes the changes of AI-391): For the execution of a call on an inherited subprogram, a call on the corresponding primitive subprogram of the parent {or progenitor} type is performed; the normal conversion of each actual parameter to the subtype of the corresponding formal parameter (see 6.4.1) performs any necessary type conversion as well. If the result type of the inherited subprogram is the derived type, the result of calling the {subprogram of the parent or progenitor} is converted to the derived type, or in the case of a null extension, extended to the derived type using the equivalent of an extension_aggregate with the original result as the ancestor_part and null record as the record_component_association_list. Change the paragraph added after 3.4(35) by AI95-00251 to read: An interface type {that} has a {progenitor type} "is derived from" that type. A derived_type_definition, however, never defines an interface type. Change the second sentence of 3.4.1(2) as modified by AI95-00251 to read: A derived type, interface type, type extension, task type, protected type, or formal derived type is also derived from every ancestor of each of its progenitor types, if any. Change 7.3(16) to read: A private extension inherits components (including discriminants unless there is a new discriminant_part specified) and user-defined primitive subprograms from its ancestor type {and its progenitor types (if any)}, in the same way that a record extension inherits components and user-defined primitive subprograms from its parent type {and its progenitor types} (see 3.4). Change 7.3(20) to read: The ancestor type specified in a private_extension_declaration and the parent type specified in the corresponding declaration of a record extension given in the private part need not be the same. If the ancestor type is not an interface type, the parent type of the full view can be any descendant of the ancestor type. In this case, for a primitive subprogram that is inherited from the ancestor type and not overridden, the formal parameter names and default expressions (if any) come from the corresponding primitive subprogram of the specified ancestor type, while the body comes from the corresponding primitive subprogram of the parent type of the full view. See 3.9.2. If the ancestor type specified in a private_extension_declaration is an interface type, the parent type can be anything so long as the full view is a descendant of the ancestor type. The progenitor types specified in a private_extension_declaration and the progenitor types specified in the corresponding declaration of a record extension given in the private part also need not be the same -- the only requirement is that the private extension and the record extension be descended from the same set of interfaces. Delete the first paragraph added after 9.1(9.1) by AI95-00345. Delete the sentence added to 9.4(11) by AI95-00345. (That, is revert to the original wording of RM95.) Change 12.5.1(5) to read: The ancestor subtype of a formal derived type is the subtype denoted by the subtype_mark of the formal_derived_type_definition. For a formal derived type declaration, the reserved words *with private* shall appear if and only if the ancestor type is a tagged type; in this case the formal derived type is a private extension of the ancestor type and the ancestor shall not be a class- wide type. Similarly, {an interface_list or} the optional reserved word abstract shall appear only if the ancestor type is a tagged type. Change the paragraph added after 12.5.1(5) by AI95-00251 to read: The actual type for a generic formal derived type shall be a descendant of every {progenitor} of the formal type. Change 12.5.1(21) to read: For a formal derived type, the predefined operators and inherited user-defined subprograms are determined by the ancestor type {and any progenitor types}, and are implicitly declared at the earliest place, if any, immediately within the declarative region in which the formal type is declared , where the corresponding primitive subprogram of the ancestor {or progenitor} is visible (see 7.3.1). In an instance, the copy of such an implicit declaration declares a view of the corresponding primitive subprogram of the ancestor {or progenitor} of the formal derived type, even if this primitive has been overridden for the actual type. When the ancestor {or progenitor} of the formal derived type is itself a formal type, the copy of the implicit declaration declares a view of the corresponding copied operation of the ancestor {or progenitor}. In the case of a formal private extension, however, the tag of the formal type is that of the actual type, so if the tag in a call is statically determined to be that of the formal type, the body executed will be that corresponding to the actual type. Change 12.5.5(4) (a section introduced by AI95-00251) to read: The actual type shall be a descendant of every {progenitor} of the formal type. !discussion (See proposal.) !example !corrigendum 3.4(01) @drepl A @fa defines a new type (and its first subtype) whose characteristics are @i from those of a @i. @dby A @fa defines a new type (and its first subtype) whose characteristics are @i from those of a parent type, and possibly from progenitor types. !corrigendum 3.4(03) @drepl The @i@fa<_subtype_indication> defines the @i; its type is the parent type. @dby The @i@fa<_subtype_indication> defines @i; its type is the @i. The @fa defines the progenitor types (see 3.9.4). A derived type has one parent type and zero or more progenitor types. !corrigendum 3.4(05) @drepl If there is a @fa, the derived type is called a @i of the parent type. A @fa shall be provided if and only if the parent type is a tagged type. @dby If there is a @fa, the derived type is called a @i of the parent type. A @fa shall be provided if and only if the parent type is a tagged type. An @fa shall be provided only if the parent type is a tagged type. !corrigendum 3.4(08) @drepl @xbullet @dby @xbullet !corrigendum 3.4(17) @drepl @xbullet, there exists a corresponding @i primitive subprogram of the derived type with the same defining name. Primitive user-defined equality operators of the parent type are also inherited by the derived type, except when the derived type is a nonlimited record extension, and the inherited operator would have a profile that is type conformant with the profile of the corresponding predefined equality operator; in this case, the user-defined equality operator is not inherited, but is rather incorporated into the implementation of the predefined equality operator of the record extension (see 4.5.2).> @dby @xbullet, there exists a corresponding @i primitive subprogram of the derived type with the same defining name. Primitive user-defined equality operators of the parent type and any progenitor types are also inherited by the derived type, except when the derived type is a nonlimited record extension, and the inherited operator would have a profile that is type conformant with the profile of the corresponding predefined equality operator; in this case, the user-defined equality operator is not inherited, but is rather incorporated into the implementation of the predefined equality operator of the record extension (see 4.5.2).> !corrigendum 3.4(18) @drepl The profile of an inherited subprogram (including an inherited enumeration literal) is obtained from the profile of the corresponding (user-defined) primitive subprogram of the parent type, after systematic replacement of each subtype of its profile (see 6.1) that is of the parent type with a @i of the derived type. For a given subtype of the parent type, the corresponding subtype of the derived type is defined as follows: @dby The profile of an inherited subprogram (including an inherited enumeration literal) is obtained from the profile of the corresponding (user-defined) primitive subprogram of the parent or progenitor type, after systematic replacement of each subtype of its profile (see 6.1) that is of the parent or progenitor type with a @i of the derived type. For a given subtype of the parent or progenitor type, the corresponding subtype of the derived type is defined as follows: !corrigendum 3.4(22) @drepl The same formal parameters have @fas in the profile of the inherited subprogram. Any type mismatch due to the systematic replacement of the parent type by the derived type is handled as part of the normal type conversion associated with parameter passing @emdash see 6.4.1. @dby The same formal parameters have @fas in the profile of the inherited subprogram. Any type mismatch due to the systematic replacement of the parent or progenitor type by the derived type is handled as part of the normal type conversion associated with parameter passing @emdash see 6.4.1. !corrigendum 3.4(23) @drepl If a primitive subprogram of the parent type is visible at the place of the @fa, then the corresponding inherited subprogram is implicitly declared immediately after the @fa. Otherwise, the inherited subprogram is implicitly declared later or not at all, as explained in 7.3.1. @dby If a primitive subprogram of the parent or progenitor type is visible at the place of the @fa, then the corresponding inherited subprogram is implicitly declared immediately after the @fa. Otherwise, the inherited subprogram is implicitly declared later or not at all, as explained in 7.3.1. !comment the paragraph added by AI-251 is deleted. !corrigendum 3.4(27) @drepl For the execution of a call on an inherited subprogram, a call on the corresponding primitive subprogram of the parent type is performed; the normal conversion of each actual parameter to the subtype of the corresponding formal parameter (see 6.4.1) performs any necessary type conversion as well. If the result type of the inherited subprogram is the derived type, the result of calling the parent's subprogram is converted to the derived type. @dby For the execution of a call on an inherited subprogram, a call on the corresponding primitive subprogram of the parent or progenitor type is performed; the normal conversion of each actual parameter to the subtype of the corresponding formal parameter (see 6.4.1) performs any necessary type conversion as well. If the result type of the inherited subprogram is the derived type, the result of calling the subprogram of the parent or progenitor is converted to the derived type, or in the case of a null extension, extended to the derived type using the equivalent of an @fa with the original result as the @fa and @b as the @fa. !corrigendum 3.4(35) @dinsa @xindent<@s9<17 If the reserved word @b is given in the declaration of a type, the type is abstract (see 3.9.3).>> @dinst @xindent<@s9<18 An interface type that has a progenitor type "is derived from" that type. A @fa, however, never defines an interface type.>> !corrigendum 3.4.1(02) @drepl A derived type is @i its parent type @i; it is derived @i from any type from which its parent type is derived. The derivation class of types for a type @i (also called the class @i at @i) is the set consisting of @i (the @i of the class) and all types derived from @i (directly or indirectly) plus any associated universal or class-wide types (defined below). @dby A derived type is @i its parent type @i; it is derived @i from any type from which its parent type is derived. A derived type, interface type, type extension, task type, protected type, or formal derived type is also derived from every ancestor of each of its progenitor types, if any. The derivation class of types for a type @i (also called the class @i at @i) is the set consisting of @i (the @i of the class) and all types derived from @i (directly or indirectly) plus any associated universal or class-wide types (defined below). !corrigendum 7.3(16) @drepl A private extension inherits components (including discriminants unless there is a new @fa specified) and user-defined primitive subprograms from its ancestor type, in the same way that a record extension inherits components and user-defined primitive subprograms from its parent type (see 3.4). @dby A private extension inherits components (including discriminants unless there is a new @fa specified) and user-defined primitive subprograms from its ancestor type and its progenitor types (if any), in the same way that a record extension inherits components and user-defined primitive subprograms from its parent type and its progenitor types (see 3.4). !corrigendum 7.3(20) @drepl @xindent<@s9<7 The ancestor type specified in a @fa and the parent type specified in the corresponding declaration of a record extension given in the private part need not be the same @emdash the parent type of the full view can be any descendant of the ancestor type. In this case, for a primitive subprogram that is inherited from the ancestor type and not overridden, the formal parameter names and default expressions (if any) come from the corresponding primitive subprogram of the specified ancestor type, while the body comes from the corresponding primitive subprogram of the parent type of the full view. See 3.9.2.>> @dby @xindent<@s9<7 The ancestor type specified in a @fa and the parent type specified in the corresponding declaration of a record extension given in the private part need not be the same. If the ancestor type is not an interface type, the parent type of the full view can be any descendant of the ancestor type. In this case, for a primitive subprogram that is inherited from the ancestor type and not overridden, the formal parameter names and default expressions (if any) come from the corresponding primitive subprogram of the specified ancestor type, while the body comes from the corresponding primitive subprogram of the parent type of the full view. See 3.9.2.>> @xindent<@s9<8 If the ancestor type specified in a @fa is an interface type, the parent type can be anything so long as the full view is a descendant of the ancestor type. The progenitor types specified in a @fa and the progenitor types specified in the corresponding declaration of a record extension given in the private part need not be the same @emdash the only requirement is that the private extension and the record extension be descended from the same set of interfaces.>> !corrigendum 9.1(9.1/1) @dinsa For a task declaration without a @fa, a @fa without @fas is assumed. @dinss For a @fa, if the first parameter of a primitive inherited subprogram is of the task type or an access parameter designating the task type, and there is an @fa for a single entry with the same identifier within the @fa, having a profile that is type conformant with that of the inherited subprogram after omitting this first parameter, the inherited subprogram is said to be @i by the conforming task entry. @i<@s8> A task declaration requires a completion, which shall be a @fa, and every @fa shall be the completion of some task declaration. Each @fa of an @fa appearing within a @fa shall denote a limited interface type that is not a protected interface. For each primitive subprogram inherited by the type declared by a @fa, at most one of the following shall apply: @xbullet @xbullet If neither applies, the inherited subprogram shall be a null procedure. !corrigendum 9.4(11) @dinsa A @fa defines a protected type and its first subtype. The list of @fas of a @fa, together with the @fa, if any, is called the visible part of the protected unit. The optional list of @fas after the reserved word @b is called the private part of the protected unit. @dinss For a @fa, the first parameter of a primitive inherited subprogram is of the protected type or an access parameter designating the protected type, and there is a @fa for a protected subprogram or single entry with the same identifier within the @fa, having a profile that is type conformant with that of the inherited subprogram after omitting this first parameter, the inherited subprogram is said to be @i by the conforming protected subprogram or entry. @i<@s8> A protected declaration requires a completion, which shall be a @fa, and every @fa shall be the completion of some protected declaration. Each @fa of an @fa appearing within a @fa shall denote a limited interface type that is not a task interface. For each primitive subprogram inherited by the type declared by a @fa, at most one of the following shall apply: @xbullet @xbullet If neither applies, the inherited subprogram is a null procedure. If an inherited subprogram is implemented by a protected procedure or an entry, then the first parameter of the inherited subprogram shall be of mode @b or @b, or an access-to-variable parameter. !corrigendum 12.5.1(05) @drepl The @i of a formal derived type is the subtype denoted by the @fa of the @fa. For a formal derived type declaration, the reserved words @b shall appear if and only if the ancestor type is a tagged type; in this case the formal derived type is a private extension of the ancestor type and the ancestor shall not be a class-wide type. Similarly, the optional reserved word @b shall appear only if the ancestor type is a tagged type. @dby The @i of a formal derived type is the subtype denoted by the @fa of the @fa. For a formal derived type declaration, the reserved words @b shall appear if and only if the ancestor type is a tagged type; in this case the formal derived type is a private extension of the ancestor type and the ancestor shall not be a class-wide type. Similarly, an @fa or the optional reserved word @b shall appear only if the ancestor type is a tagged type. The actual type for a generic formal derived type shall be a descendant of every progenitor of the formal type. !corrigendum 12.5.1(21) @drepl For a formal derived type, the predefined operators and inherited user-defined subprograms are determined by the ancestor type, and are implicitly declared at the earliest place, if any, within the immediate scope of the formal type, where the corresponding primitive subprogram of the ancestor is visible (see 7.3.1). In an instance, the copy of such an implicit declaration declares a view of the corresponding primitive subprogram of the ancestor of the formal derived type, even if this primitive has been overridden for the actual type. When the ancestor of the formal derived type is itself a formal type, the copy of the implicit declaration declares a view of the corresponding copied operation of the ancestor. In the case of a formal private extension, however, the tag of the formal type is that of the actual type, so if the tag in a call is statically determined to be that of the formal type, the body executed will be that corresponding to the actual type. @dby For a formal derived type, the predefined operators and inherited user-defined subprograms are determined by the ancestor type and any progenitor types, and are implicitly declared at the earliest place, if any, immediately within the declarative region in which the formal type is declared, where the corresponding primitive subprogram of the ancestor or progenitor is visible (see 7.3.1). In an instance, the copy of such an implicit declaration declares a view of the corresponding primitive subprogram of the ancestor or progenitor of the formal derived type, even if this primitive has been overridden for the actual type. When the ancestor or progenitor of the formal derived type is itself a formal type, the copy of the implicit declaration declares a view of the corresponding copied operation of the ancestor or progenitor. In the case of a formal private extension, however, the tag of the formal type is that of the actual type, so if the tag in a call is statically determined to be that of the formal type, the body executed will be that corresponding to the actual type. !corrigendum 12.5.5(01) !comment Just a dummy to trigger a conflict; the wording change is only in !commentt paragraph 4. @drepl The actual type shall be a descendant of every ancestor of the formal type. @dby The actual type shall be a descendant of every progenitor of the formal type. !example (See AI-251 and AI-345 for examples.) !ACATS test This is just terminology and minor glitches; any ACATS tests should be covered by those for the original AIs. !appendix From: Erhard Ploedereder Sent: Tuesday, May 3, 2005 12:05 PM The connection between "implemented_by" and "overridden by parent" that I am/was worried about...... below is a description of what I was worried about -- however, meanwhile I discovered that one sentence may save the day, i.e., makes my worrisome examples illegal. The sentence is 3.4 (5/2, 2.sentence) in the version as I have corrected the sentence. If the original version stays, you definitely need to read on. The (corrected) sentence expresses the rule: have an interface_list in a derived type definition, then the parent must be tagged (which a task type is not). The original rule poses the rule only if it is a record extension part as well, making my examples legal (and troublesome). The situation that I worried about starts out by the following: task type T is entry do_this; end T; interface I; procedure do_this(X: I); type NT is new T and I; vs. task type NNT is new I with entry do_this; end NNT; Compare NNT and NT. For NNT, clearly the entry implements I.do_this. For NT, does it, too? It looks like "no, it doesn't" since the entry of NT isn't declared top-level and hence no homograph to the inherited I.do_this. Lawyerly logical, "userly" a surprise when it works for NNT, but not for NT. If this isn't fixed (or isn't so in the first place) then the one that really worries me lawyerly and userly is type N3T is new NT and NI; - NI a descendant of I For N3T, although the entry NT.do_it implements I.do_it, this won't be recognized and there will be an obligation to override NI.do_it, although it is I.do_it. --- by a rather narrow margin, the following problem is avoided because prefixed views require a tagged type object, which tasks aren't, and hence prefix call notation is not available for the call to a subprog inherited from an interface. X: N3T; ... X.do_it; could be either a call on the entry or a prefix-notation call to the inherited (and overridden) NI.do_it. (Of course, with a "unified view" it wouldn't be a problem, since it's all one and the same.) **************************************************************** From: Randy Brukardt Sent: Tuesday, May 3, 2005 1:38 PM I think your version is what was intended; there is no intention to allow derivation from a task type that has interfaces, or to allow derivation to add interfaces without extension. So we can ignore the rest of your explanation. Thank goodness. :-) **************************************************************** From: Erhard Ploedereder Sent: Monday, May 2, 2005 8:38 PM At the Paris meeting, I took an action item to check for overall consistency of parent vs. ancestor vs. progenitor, etc etc, i.e., the whole terminology that surrounds inheritance. Here is the outcome in sequence of the manual D-11, not necessarily importance, although the first ones are pretty significant; Pascal may rule one or the other out of bounds. Erhard ------------------------- Comments on Interface Semantics: ------------------------------- I was unable to find a rule that allowed abstract subprograms inherited from interfaces to be implemented by subprograms inherited from the parent. Such a rule is essential and certainly is not derivable -quite the contrary- from current abstract subprogram rules. Presently, it seems that all the subprograms inherited from interfaces need to be explicitly implemented in a concrete descendant type (since they are treated as inherited abstract subprograms which must be overridden). THIS CANNOT POSSIBLY BE THE INTENT OF INTERFACES. Note in particular the interesting situation where a concrete parent type implements Interface I and the derived type implements NI, a descendant of I. Here it is unavoidable that an implementation from a parent meets a spec from a progenitor. This must be legal and there mustn't be a need to override again what is already implemented for the parent. The very thought that the accumulated load of subprogs of a specialized interface needs to be explicitly wrapered again, merely because I wanted to add the 3 very special routines there to my class hierarchy, is daunting. ----------------- I did not find a rule that explained what happens when "almost identical" subprogs are inherited from multiple interfaces, i.e., is it o.k. to multiply inherit the same or mode- or subtype- or type-conformant signatures? (While this question was a bit pathological in the context of homographs created by generic instantiations where I believe calls become ambiguous, here it will be commonplace and there better be an answer that allows them without any problems, signature of the overriding winning in case of differences) ----------------- I did not find a rule that allowed multiple subtype-conformant specs inherited from multiple progenitors to be implemented by a single overriding declaration. (Maybe none is needed; the words for the "implemented-by" synchronized interfaces seem to work for multiple specs. The words for abstract subprograms are not so resistant to doubt. ) -------------------- 3.9.2 (20) dispatches only to overriding bodies, not to "implemented by" bodies. So dispatching doesn't work for implemented-by ops of synchronized interfaces. Fix 3.9.2(20) to include both kinds. ... or ... Ceterum Censeo and hence you can ignore: to use the overriding_indicator "overriding" for "implemented-by" bodies and, at the same time, make a semantic rule that "implemented-by" is not "overriding", is atrocious language design. PLEASE; PLEASE, UNDO THIS STUPID DISTINCTION BETWEEN OVERRIDING AND IMPLEMENTED-BY. It'll bite again and again. Make it a principle that one dispatches (only) to overriding subprogs (if and only if!) --------------------------- 3.4 (1.a); N (13): Glossary...is a bit wrong now. 3.4 Rule is missing that interface lists allow only interface types !!!! (was found in 3.9.*; will be solved per decision at Paris mtg) 3.4 (5) should say "record_extension_part or an interface_list" (and-> or; no parenthesis; Reason: simple logic) 3.4(15/2) "appears" (where?) -> "is present in the derived_type_definition." ----- 3.4 (16) are there no predefined ops for interface types ? '(17/2) for equality, the rules are wrong!!! consider: parent is limited interface; some progenitor has a user-defined "=" (but is limited); does the resulting type inherit "=" ? The rules say "no", they should say "yes", because the progenitor has equality (and the type better not be without the "="!!!) Does it matter beyond the limited type case? Probably not, because the new type has its own predefined equality, which now implements the "="; unless... (17/2) if there are other predefined primitive ops for interfaces, they need to be dealt with here. Are there? ----- 3.4 (21) what if the parent type is an interface type, as is allowed ? Then the sentence makes no sense, since the parent (sub)type should be seen as infinite, should it not? If the sentence should apply to interface parents, the principle should apply to the interfaces inherited from progenitors as well in the context of the co-variant inheritance. Admittedly, it seems to be trivially true today, since interfaces don't have discriminants; but I am bothered by "shortening" the rule just because this is how it is in today's definition. The "convertability" ought to be to all ancestors. ------ 3.9 terminology: interface vs interface type ... note that normally the prefix term is reserved for the objects of the type. E.g., the task X of task type T. Here "interface" is sometimes used to mean interface type. 1.b.2/2; 12.a/2, 3.9.1 4.m/2, .... Should be fixed. The fact that the abbreviation is in fact defined (in 3.9.4 (3/2)), makes it legit, but still not helpful, just lazy. X: I; -- the interface X of interface I ?????? ------- 3.9.(1k) 2.line "parent" -> "parent or progenitor"; the rule applies to the inherited subprograms originating from normal progenitors as well. Anything to be stated about synchronized progenitors? ---------- 3.9. 2/2 typo: ...interface type is [a] tagged type. -------- 3.9.1 3/2 2.sentence : parent -> the parent or a progenitor type !! (the sentence definitely applies to both) (Remember: it is possible that the parent type is an interface, so it is legit to have a limited parent, but a non-limited type here.) --------- 3.9.2(10/1) <<>> However there is one case remaining: inheriting a null procedure from a progenitor or redefining it: what is the Convention? So far, 3.9.2(10/1) only talks about inheriting from parent types. --------- 3.9.3 (4/2) I could not parse the sentence in less than 3 minutes (and still am unsure of what it tries to say); an earlier comment said that I could not parse it at all. 3 minutes helped. Still unsure what the "or a type other than..." binds to (the type, the parent or the ancestor type or none of the above). Now I need 30 minutes of semantic analysis. :-) --------- The argument in 4.5.2 (14.b/2) seems wrong: formal derived types can also have parent types. Are they full types? -------- 4.6 23.b "parent -> ancestor" ! because conversions also from progenitors (in fact ancestors in general) --------- 7.3.(20) : "definitely to be extended to the progenitors !" is my original note. 20.1/2 does that. What remains is some worry that the words in 20 may be wrong in case the parent is an interface type. --------- 7.5 (2/2) : no brackets please (where is it derivable from?) --------- 7.6 Could Controlled become interface now ???? I know that this is "far out", but it would do away with the bloody nuisance that Controlledness cannot be mixed-in today. (It would require "magic" by the compiler, since it presumably involves data. Just a thought and yes, shoot the engineer.) ---------- 9.1 (9.2/2) typo: profile of [the] inherited subprog ------ 9.1 (9.d/2) what is a name interface? how does the first part of the sentence parse? missing "a" in "Only [a] limited" same comments for 9.4 11.d/2 ------- 12.5.1 (5.1/2) actual must descend all progenitors, but NOT the parent ??? (while later rules deal with the parent, it is really bad description to not include the parent here) ------ 12.5.1. (21/2) typo "progentor"; typo missing blank in "ancestoror" The content is bogus; apparently the "and progenitor" in the first sentence was added in the wrong place. Should be added after "by the ancestor type" and should say "and any progenitor types" there is a displaced blank later " or progenitoris" 12.5.1. (21.a/2) typo "progentor" and double blank before it --------- A (musing) observation: Interface Interf; procedure foo(X: Interf) is null; procedure bar(X: Interf); seems that just about the only place in the code where I can mutter "Interf" is in an interface_list. Everywhere else, it's Interf'Class to be usable at all, since I cannot have objects of type Interf. Correct? If not so, enlighten me. But then, it'll come as a bloody surprise that procedure test(X: Interf) is begin bar(X); -- dispatching call, of course foo(X); -- does not dispatch; does nothing end test; I wanted a "null" at the bottom of a dispatching wrapper hierarchy, but this defeats the very idea and makes the call non-dispatching. ---------------- **************************************************************** From: Randy Brukardt Sent: Monday, May 2, 2005 10:24 PM > I did not find a rule that explained what happens when "almost > identical" subprogs are inherited from multiple interfaces, i.e., is > it o.k. to multiply inherit the same or mode- or subtype- or > type-conformant signatures? (While this question was a bit > pathological in the context of homographs created by generic > instantiations where I believe calls become ambiguous, here it will be > commonplace and there better be an answer that allows them without any > problems, signature of the overriding winning in case of differences) That's the purpose of 8.3(12.1-12.3 and 26.1-26.2). I suggest reading these rules carefully to see if they cover this and other questions that you raise here, and (if your head doesn't explode first ;-), report your findings. (I believe that they do, but I have to save my head for the AARM and all of the other editorial comments.) > > ----------------- > > I did not find a rule that allowed multiple subtype-conformant specs > inherited from multiple progenitors to be implemented by a single > overriding declaration. (Maybe none is needed; the words for the > "implemented-by" synchronized interfaces seem to work for multiple specs. > The words for abstract subprograms are not so resistant to doubt. ) See above. It seems that you missed 8.3 altogether. > -------------------- > > 3.9.2 (20) dispatches only to overriding bodies, not to "implemented > by" bodies. So dispatching doesn't work for implemented-by ops of > synchronized interfaces. Fix 3.9.2(20) to include both kinds. This looks like a hole. > ... or ... > > Ceterum Censeo and hence you can ignore: to use the > overriding_indicator "overriding" for "implemented-by" bodies and, at > the same time, make a semantic rule that "implemented-by" is not > "overriding", is atrocious language design. PLEASE; PLEASE, UNDO > THIS STUPID DISTINCTION BETWEEN OVERRIDING AND IMPLEMENTED-BY. It'll > bite again and again. Make it a principle that one dispatches (only) to > overriding subprogs (if and only if!) I can't ignore it, I don't know Latin. (A US public school education. :-) I think this is a damned if you do, damned if you don't situation. The differences between "overriding" and "implemented by" are significant enough that we *have* to have some terminological difference between them. I suppose we could stick some sort of adjective on "overriding", say "normal overriding" and "prefixed overriding", and then try to make all of the rules work. But if we do that, I hope Ada Europe has some more money to spend on this, because it would take a lot of work to carefully check everything several more times. I'll leave someone else to look over the rest of it. (Or do it some other time; say September.) **************************************************************** From: Tucker Taft Sent: Monday, May 2, 2005 11:29 PM > 3.9.2 (20) dispatches only to overriding bodies, not to "implemented > by" bodies. So dispatching doesn't work for implemented-by ops of > synchronized interfaces. Fix 3.9.2(20) to include both kinds. Good point. We should include bodies that are implemented by protected operations or task entries as legitimate, albeit compiler-generated, bodies. > > .. or .... > > Ceterum Censeo and hence you can ignore: to use the > overriding_indicator "overriding" for "implemented-by" bodies and, at > the same time, make a semantic rule that "implemented-by" is not > "overriding", is atrocious language design. PLEASE; PLEASE, UNDO > THIS STUPID DISTINCTION BETWEEN OVERRIDING AND IMPLEMENTED-BY. It'll > bite again and again. Make it a principle that one dispatches (only) to > overriding subprogs (if and only if!) I understand your concern, but as Randy points out, the distinction is quite important. The notion of a compiler-provided "wrapper" is pretty fundamental for the semantics to make sense here. For example, the formal parameter names and the defaults come from the inherited subprogram, not from the protected/task op, when you call this operation in non-prefix notation. That pretty much has to be true since the protected/task op doesn't even have a formal parameter name for the first parameter. It is unfortunate that the overriding indicator mucks up the terminology situation even further. Perhaps Randy's idea of calling it "prefixed overriding" could help, though I think he is right that we could run into more confusion that way (analogous to the old confusion that a "generic package" is *not* a package). On the issue of interface semantics in general, as Randy pointed out, you seem to have missed 8.3 completely, and that is where all the tricky rules are that relate to inheritance and overriding between multiple interface ancestors. It sounds like we should have some sort of "pointer" to 8.3 from 3.9.4 (or 3.4), and/or summarize the rules in 3.9.4 (or 3.4) in AARM redundancy brackets. **************************************************************** From: Erhard Ploedereder Sent: Tuesday, May 3, 2005 6:28 AM > I understand your concern, but as Randy points out, the distinction > (of "overriding" and "implemented-by") is quite important. No argument given. Of course it is important...however, distinction does not necessarily imply exclusion. For example, call it "protected overriding" or "synchronized overriding" (and all overriding rules apply, except for the ones that "implemented by" presently modifies; the only other place to also modify is the identified one on Convention). Actually, give me an answer to comment Uno first, which does have a bearing on the problem: are we serious that ops inherited from an interface cannot be implemented by the parent? If this is so, the interface capability is about as useful as Ada95 OOP was a few months prior to its release, when it didn't work at all for objects designated by access values. If this is not so, one needs to deal with interface ops implemented by inherited protected ops anyway, which equally require "implemented-by" rules. (I'll react to the 8.3 pointer separately.) **************************************************************** From: Tucker Taft Sent: Tuesday, May 3, 2005 7:53 AM > ... > Actually, give me an answer to comment Uno first, which does have a bearing > on the problem: are we serious that ops inherited from an interface cannot > be implemented by the parent? This sentence sounds like the famous "when did you stop beating your wife?" question. The answer is that a non-abstract operation inherited from one ancestor can override an abstract operation inherited from another. This is based on 8.3(12.1/2...): If two or more homographs are implicitly declared at the same place: * If one is a non-null non-abstract subprogram, then it overrides all [which] {that} are null or abstract subprograms. * If all are null procedures or abstract subprograms, then any null procedure overrides all abstract subprograms; if more than one homograph remains that is not thus overridden, then one is chosen arbitrarily to override the others. There are some legality rules in 8.3(26.1/2) which guarantee when one is chosen arbitrarily, the choice doesn't matter, because they all must conform fully in that case. But the rule you care about is the first one, namely a non-null, non-abstract inherited subprogram overrides all the null or abstract ones. That gives you the effect you (and we all) want. > ... If this is so, the interface capability is > about as useful as Ada95 OOP was a few months prior to its release, when it > didn't work at all for objects designated by access values. If this is not > so, one needs to deal with interface ops implemented by inherited protected > ops anyway, which equally require "implemented-by" rules. I don't see the connection. The "implemented-by" rules for protected/task operations rely on a "wrapper" model, somewhat analogous to the way renaming-as-body is described. The rules given in 8.3 are about one inherited operation overriding another. The wording in 3.9.2(20), which doesn't seem to work very well for the "implemented-by" case, does seem to handle adequately this case of one inherited operation overriding another (surprisingly). **************************************************************** From: Pascal Leroy Sent: Monday, May 9, 2005 10:29 AM > 3.9.2 (20) dispatches only to overriding bodies, not to > "implemented by" bodies. So dispatching doesn't work for > implemented-by ops of synchronized interfaces. Fix 3.9.2(20) > to include both kinds. Yep, that's a bug. > PLEASE; PLEASE, UNDO THIS STUPID DISTINCTION BETWEEN > OVERRIDING AND IMPLEMENTED-BY. It'll bite again and again. > Make it a principle that one dispatches (only) to overriding > subprogs (if and only if!) Let's not go there. As others have indicated, the difference is very significant from the language design standpoint. Hopefully users should be able to ignore it most of the time, which is why I think that overriding_indicator is fine. Maybe this could have been better described by a 2-step mechanism: (1) the inherited operation is *overridden* by an implicit, subtype conformant subprogram and (2) this implicit subprogram is *implemented by* calling the entry or protected operation with the appropriate parameter mucking (and possibly different defaults, convention, etc.). This would have made the wrapper model explicit, and dispatching would have worked "naturally". However it is too late to revise the wording in this direction, and this is insufficiently broken anyway. > 3.4 (5) should say "record_extension_part or an > interface_list" (and-> or; > no parenthesis; Reason: simple logic) In a subsequent message you wrote "have an interface_list in a derived type definition, then the parent must be tagged (which a task type is not)". Hmm. Unfortunately, a task type *may* be tagged, precisely if it is derived from one or more interfaces (that's important for Class to work, see 3.9(2.1)). So we need to say that the parent is not a tagged task or protected type. It would be simple enough to say "if and only if the parent type is a tagged record type". But what about privacy? Consider: package P is type T is tagged limited private; private task type T is new I with ... end P; type NT is new P.T with ...; -- Legal? Hopefully not! HELP! What is it that makes NT illegal? > 3.4 (16) are there no predefined ops for interface types ? > '(17/2) for equality, the rules are wrong!!! > consider: parent is limited interface; some progenitor has a > user-defined "=" (but is limited); > does the resulting type inherit "=" ? > The rules say "no", they should say "yes", because the > progenitor has equality (and the type better not be > without the "="!!!) I think you have a point here. One option would be to say that if some progenitor has an "=" (user-defined or not) you cannot use it to declare a limited type. Another option would be to say that if you declare a limited type you inherit any user-defined "=" as you would inherit any other user-defined operation. I think I'd prefer the 1st option, as it would make the rules more uniform. > 3.9.1 3/2 2.sentence : parent -> the parent or a progenitor type !! > (the sentence definitely applies to both) Agreed. > 3.9.3 (4/2) I could not parse the sentence in less than 3 > minutes (and still am unsure of what it tries to say); Fair enough, but you cannot just groan, you have to propose a rewrite (have fun). > The argument in 4.5.2 (14.b/2) seems wrong: formal derived > types can also have parent types. Are they full types? Formal derived types don't have a parent, only a "ancestor subtype" (12.5.1(5/2)). > 7.6 Could Controlled become interface now ???? It would be nice, but there are nasty problems associated with that. For instance, you would have to assume that any object of a class-wide type could be controlled, because Controlled could be mixed in at any level. This wouldn't help code performance. > 9.1 (9.d/2) > what is a name interface? how does the first part of the > sentence parse? > missing "a" in "Only [a] limited" Here "name" is a verb and "interfaces" a noun. I think the sentence is fine. > 12.5.1. (21/2) typo "progentor"; typo missing blank in "ancestoror" > The content is bogus; apparently the "and progenitor" in the > first sentence was added in the wrong place. Right. The AI has the correct text. Randy was tired. > Interface Interf; > procedure foo(X: Interf) is null; > procedure bar(X: Interf); (I won't comment on the weird syntax for declaring Interf ;-) Bar must be abstract, because null and abstract are your only choices for primitive subprograms of interfaces. Therefore... > But then, it'll come as a bloody > surprise that procedure test(X: Interf) is begin > bar(X); -- dispatching call, of course > foo(X); -- does not dispatch; does nothing > end test; ... this is not even legal because the non-dispatching call to (the presumably abstract) Bar is a no-no. So no bloody surprise here. **************************************************************** From: Bob Duff Sent: Monday, May 9, 2005 1:59 PM > package P is > type T is tagged limited private; > private > task type T is new I with ... > end P; > > type NT is new P.T with ...; -- Legal? Hopefully not! > > HELP! What is it that makes NT illegal? Isn't that illegal by 3.9.4(10/2) (in the 13 April 2005 version)? > > 3.4 (16) are there no predefined ops for interface types ? > > '(17/2) for equality, the rules are wrong!!! > > consider: parent is limited interface; some progenitor has a > > user-defined "=" (but is limited); > > does the resulting type inherit "=" ? > > The rules say "no", they should say "yes", because the > > progenitor has equality (and the type better not be > > without the "="!!!) > > I think you have a point here. One option would be to say that if some > progenitor has an "=" (user-defined or not) you cannot use it to declare a > limited type. Another option would be to say that if you declare a > limited type you inherit any user-defined "=" as you would inherit any > other user-defined operation. I think I'd prefer the 1st option, as it > would make the rules more uniform. > I think I prefer the 2nd option. Ada 95 allows user-defined "=" on limited types, and it can be inherited. Wouldn't the 1st option above seem like an annoying arbitrary restriction? > > 7.6 Could Controlled become interface now ???? > > It would be nice, but there are nasty problems associated with that. For > instance, you would have to assume that any object of a class-wide type > could be controlled, because Controlled could be mixed in at any level. > This wouldn't help code performance. Isn't that already the case, since you can add controlled components? So even if the class-wide type is not itself controlled, it might need finalization. (Yes, I agree that's an annoying efficiency hit -- but I think it already exists.) **************************************************************** From: Robert I. Eachus Sent: Monday, May 9, 2005 2:21 PM Pascal Leroy wrote: >>3.4 (16) are there no predefined ops for interface types ? >> '(17/2) for equality, the rules are wrong!!! >> consider: parent is limited interface; some progenitor has a >> user-defined "=" (but is limited); >> does the resulting type inherit "=" ? >> The rules say "no", they should say "yes", because the >> progenitor has equality (and the type better not be >> without the "="!!!) >> >> > >I think you have a point here. One option would be to say that if some >progenitor has an "=" (user-defined or not) you cannot use it to declare a >limited type. Another option would be to say that if you declare a >limited type you inherit any user-defined "=" as you would inherit any >other user-defined operation. I think I'd prefer the 1st option, as it >would make the rules more uniform. And I prefer the second option, as I think it provides a necessary capability. Where you need it, it would be frustrating and error prone not to allow a user defined "=" to be inherited. There are cases where copying of a type makes no sense, but equality (actually identity) is well defined and needed. For example, I have some code that has to check if two elements of a task type are the same task. (Yes, it is checking an error case where two parameters shouldn't be the same task from an array, but it is much clearer to pass the task objects as parameters rather than their indexes. And of course due to the nature of abstractions, you have nested calling, etc. ) So in this case, the *with* (or if you prefer the *use*) of Task_Identity is limited to the definition of "=" not spread throughout the code. Yes, I could overload some other operator, but we really shouldn't be going there. **************************************************************** From: Tucker Taft Sent: Monday, May 9, 2005 3:05 PM ... > I think I prefer the 2nd option. Ada 95 allows user-defined "=" on > limited types, and it can be inherited. Wouldn't the 1st option above > seem like an annoying arbitrary restriction? I agree with Bob. There should not be anything special about "=" -- if you extend a limited interface that has "=" then you must override it. I'm a little unclear why we need to say anything. Clearly if it has a predefined "=", then it is not a limited interface, so we are only talking about user-defined "=". >>>7.6 Could Controlled become interface now ???? >> >>It would be nice, but there are nasty problems associated with that. For >>instance, you would have to assume that any object of a class-wide type >>could be controlled, because Controlled could be mixed in at any level. >>This wouldn't help code performance. > > Isn't that already the case, since you can add controlled components? > So even if the class-wide type is not itself controlled, it might need > finalization. (Yes, I agree that's an annoying efficiency hit -- but I > think it already exists.) Efficiency isn't the only problem. J.P. Rosen brought this up several months ago, and alas, we had some pretty convincing reasons why we couldn't do this. The main reason is that we don't allow "hidden" interfaces. Right now, there are plenty of types that hide the fact they are controlled (e.g. Unbounded_String is likely to be implemented that way). These would all become illegal if we made Controlled into an interface. I tried to rethink whether we could allow "hidden" interfaces under certain circumstances, such as when all the primitives of the interface are null procedures, but that really didn't solve the problem. The main issue is that if a type has a hidden interface, and then you extend the type and add an explicit derivation from that same interface, you are likely going to screw things up with the implementation of the hidden interface. In particular, if someone implemented Controlled in a hidden way and defined Finalize to do something, if you then on some extension implement Controlled visibly, and override Finalize to do something completely different, the "hidden" Finalize operation is no longer going to be performed. **************************************************************** From: Gary Dismukes Sent: Monday, May 9, 2005 3:30 PM > > HELP! What is it that makes NT illegal? > > Isn't that illegal by 3.9.4(10/2) (in the 13 April 2005 version)? Right, that prevents it. > > I think you have a point here. One option would be to say that if some > > progenitor has an "=" (user-defined or not) you cannot use it to decl are > > a limited type. Another option would be to say that if you declare a > > limited type you inherit any user-defined "=" as you would inherit any > > other user-defined operation. I think I'd prefer the 1st option, as it > > would make the rules more uniform. > > I think I prefer the 2nd option. Ada 95 allows user-defined "=" on > limited types, and it can be inherited. Wouldn't the 1st option above > seem like an annoying arbitrary restriction? The second option seems better to me as well (more natural and flexible). > > > 7.6 Could Controlled become interface now ???? > > > > It would be nice, but there are nasty problems associated with that. For > > instance, you would have to assume that any object of a class-wide type > > could be controlled, because Controlled could be mixed in at any level. > > This wouldn't help code performance. > > Isn't that already the case, since you can add controlled components? > So even if the class-wide type is not itself controlled, it might need > finalization. (Yes, I agree that's an annoying efficiency hit -- but I > think it already exists.) As Tuck points out (and as I was about to before I saw his message...), that's a no-go (because of the 3.9.4(10/2) rule you cited:). Oh well. If it weren't for that restriction, I think you're right that the implementation wouldn't add any significant difficulty beyond what compilers already have to do for handling finalization on class-wide objects. **************************************************************** From: Randy Brukardt Sent: Monday, May 9, 2005 3:50 PM > As Tuck points out (and as I was about to before I saw his message...), > that's a no-go (because of the 3.9.4(10/2) rule you cited:). Oh well. > If it weren't for that restriction, I think you're right that the > implementation wouldn't add any significant difficulty beyond what > compilers already have to do for handling finalization on class-wide > objects. I disagree, there is a lot more complication. That's because type Controlled may have (hidden) components to provide the control data necessary for finalization. (I'd expect that to be a likely implementation, but I don't know of other compilers do that.) Interfaces don't have data. Making Controlled an interface would require the compiler to have some way to handle having data for interfaces. It would be aggrevating to have to implement that fully *and* not be able to use it for any other purpose. In addition, as Tucker pointed out, interfaces are essentially a Boolean proposition: either they are present or they are not present. That means that there is no way to deal with hidden overriding routines (there can be only one overriding), and certainly we don't want the incompatibility of requiring all controlled types to be visible. **************************************************************** From: Gary Dismukes Sent: Monday, May 9, 2005 6:41 PM > I disagree, there is a lot more complication. That's because type Controlled > may have (hidden) components to provide the control data necessary for > finalization. (I'd expect that to be a likely implementation, but I don't > know of other compilers do that.) Interfaces don't have data. Making > Controlled an interface would require the compiler to have some way to > handle having data for interfaces. It would be aggrevating to have to > implement that fully *and* not be able to use it for any other purpose. You're probably right that it has significant additional complications because of internal components on the root types. Anyway, I wasn't trying to argue in favor of making those types interfaces, just that I thought it wouldn't be that hard if we didn't have the restriction (looks like I was probably wrong about that). > In addition, as Tucker pointed out, interfaces are essentially a Boolean > proposition: either they are present or they are not present. That means > that there is no way to deal with hidden overriding routines (there can be > only one overriding), and certainly we don't want the incompatibility of > requiring all controlled types to be visible. I wouldn't say there's no way of dealing with hidden overriding operations. The AARM discusses the alternatives and explains that the restriction is basically a methodological one. The outside one could be defined to override, but I agree that we don't want that. In short, I agree with the restriction requiring that inheritance from interfaces be visible. **************************************************************** From: Bob Duff Sent: Monday, May 9, 2005 7:29 PM > Efficiency isn't the only problem. I wasn't arguing in favor of making Limited_[Controlled] an interface -- just commenting on the efficiency point. If nothing else, it's too late. I'm still interested in the technical issue, though. In particular, if we weren't worried about compatibility, would the best solution be to make these types interfaces, and thereby forbid hidden controlledness? (I understand Randy's point about data in these types.) **************************************************************** From: Randy Brukardt Sent: Monday, May 9, 2005 7:49 PM I think so, actually. I could imagine handling the data through background magic of some sort, and that wouldn't be so nasty if it had been designed in from the beginning. Similarly, if we weren't worried about compatibility at all, we'd probably use some other mechanism for data hiding (this one is pretty messy for OO), and hopefully eliminate problems with hidden inferfaces (so those would be allowed). [One possibility would be automated composition of primitives.] But of course that wouldn't resemble Ada that much (at least semantically), so this is rather off-topic. **************************************************************** From: Tucker Taft Sent: Monday, May 9, 2005 10:24 PM The "best" possible solution probably makes Initialize and Finalize into user-specifiable attributes, analogous to Read and Write. And they would probably automatically compose, etc. **************************************************************** From: Robert I. Eachus Sent: Monday, May 9, 2005 11:33 PM Is that a proposal for Ada1Z? **************************************************************** From: Tucker Taft Sent: Monday, May 9, 2005 11:50 PM Actually, it was a proposal for Ada 9X which never made it. **************************************************************** From: Pascal Leroy Sent: Tuesday, May 10, 2005 4:23 AM > And I prefer the second option, as I think it provides a necessary > capability. Where you need it, it would be frustrating and error prone > not to allow a user defined "=" to be inherited. There are cases where > copying of a type makes no sense, but equality (actually identity) is > well defined and needed. That makes sense. Remember though that there is no code associated with interfaces, so any user-defined "=" has to be abstract. So this capability is quite limited, although I can see that it would be useful at times. **************************************************************** From: Robert I. Eachus Sent: Tuesday, May 10, 2005 5:28 PM I did a double-take when I read that, then realized (I hope) that Pascal meant to say that subprograms in interfaces are abstract and do not have explicit bodies. Or am I wrong? I looked at AI-251, and it says: All primitive subprograms must be abstract or null. Seems pretty clear. But haven't we been assuming that there is a (non-abstract) equality for non-limited interfaces? It is probably reasonable to assume that 'Read and 'Write for interface types are abstract, but what about 'Class'Read and 'Class'Write? I had just assumed that I could write: package P is type I is interface; package Ops is function "="(Left, Right: I) return Boolean is begin return Left'Address = Right'Address; end "=" end Ops; end P; It would be nice to be able to dispense with the package Ops, but now I am begining to wonder if there are any predefined operations or attributes for interface types. **************************************************************** From: Randy Brukardt Sent: Tuesday, May 10, 2005 5:42 PM > Seems pretty clear. But haven't we been assuming that there is a > (non-abstract) equality for non-limited interfaces? Predefined-equality is weird, but Tucker concluded it worked OK. And interfaces make my head hurt. :-) > It is probably > reasonable to assume that 'Read and 'Write for interface types are > abstract, but what about 'Class'Read and 'Class'Write? The stream attributes have their own rules; they're always around, but in some cases you can't call them. There was little consideration of abstractness and stream attributes in the first place. Anyway, go read the availability rules in 13.13.2(39-49/2) in the draft 11 AARM. You should be able to figure out any answer about stream attributes there (although they might make *your* head hurt); if you can't, then tell us what you think the hole is. **************************************************************** From: Robert I. Eachus Sent: Tuesday, May 10, 2005 9:56 PM >Predefined-equality is weird, but Tucker concluded it worked OK. And >interfaces make my head hurt. :-) > I think the problem is that the reserved word appears in the wrong place. Technically interface *types *appear in interface* packages. *How do you know that a package is an interface package? Because it contains interface types. I would much prefer for the syntax to require: *interface package* Foo *is*... *type* Bar *is interface*;... *end* Foo; Just syntactic sugar, but it would make it clear that this is a funny kind of package because it contains interfaces. >Anyway, go read the availability rules in 13.13.2(39-49/2) in the draft 11 >AARM. You should be able to figure out any answer about stream attributes >there (although they might make *your* head hurt); if you can't, then tell >us what you think the hole is. I'll do that, and if I have trouble I'll tell you where the new hole in my head is. ;-) **************************************************************** From: Pascal Leroy Sent: Wednesday, May 11, 2005 3:53 AM > Seems pretty clear. But haven't we been assuming that there is a > (non-abstract) equality for non-limited interfaces? There is certainly a non-abstract equality for non-limited interface, and it returns True (much like for a null record) and it get incorporated into the "=" for types whose parent is the interface. > It is probably > reasonable to assume that 'Read and 'Write for interface types are > abstract, but what about 'Class'Read and 'Class'Write? Don't assume anything! For interfaces, just like for any good old abstract type, 'Read and 'Write are non-abstract, and they read/write the non-tag components. Not too useful because there isn't any, but well-defined. 'Class'Read and 'Class'Write dispatch, so they never call the attributes for an interface. Fine. 'Input and 'Output behave much like 'Read and 'Write, 'cause there isn't any discriminant to read/write. 'Class'Input and 'Class'Output read/write the tag of the parameter (which is never the tag of an interface) and then dispatch. Fine. So I see no definitional problem here. > I had just assumed that I could write: > > package P is > > type I is interface; > > package Ops is > function "="(Left, Right: I) return Boolean is > begin return Left'Address = Right'Address; end "=" > end Ops; > > end P; > > It would be nice to be able to dispense with the package Ops, > but now I > am begining to wonder if there are any predefined operations or > attributes for interface types. The above is certainly legal, but the "=" is not primitive and hence not inherited. It can only be called explicitly using view conversions. **************************************************************** From: Robert I. Eachus Sent: Wednesday, May 11, 2005 2:10 PM >The above is certainly legal, but the "=" is not primitive and hence not >inherited. It can only be called explicitly using view conversions. Understood. I don't know why this misfeature is required (I must declare a non-primitive "="), but it is. Yes, I know that it would 'cause problems' if a derived type inherited two non-abstract subprograms with the same signature. I just think that requiring such subprograms to be overridden--i.e. make them abstract--would be much more user friendly. However, what I was getting at in the example was that I should be able to (and I can) write subprogram definitions that take parameters of the interface type, and use attirbutes and subprograms with that notation (such as Inf'Read) inside the subprogram body. This rule means that I will have to write 'wrappers' to make the calls look reasonable, but at least I can have an interface that results in *one* copy of the actual algorithm. I can write a package that provides a sort routine through inheritance? package Quicksort is type Sortable is interface; package Ops is function ">"(L,R: Sortable) return Boolean; end Ops; generic type Index is (<>); type Element is private; type To_Be_Sorted is new Sortable; with function Indexed_Element(S: To_Be_Sorted; I: Index) return Element; with procedure Set_Element(To: Element; S: in out To_Be_Sorted; I: Index); procedure Sort(Arr: in out To_Be_Sorted); end Quicksort; Not too painful, and I hope legal. Hmmm. The more elegant approach may be: package Sortable_Arrays is type Element is interface; type Array_Of is array (Integer range <>) of Element'Class; generic with function ">"(L,R: Element) return Boolean; procedure Sort(Arr: in out Array_Of); end Sortable_Arrays; But I have a suspicion that even if this 'works' there will be too much unnecessary tag checking to be efficient. We then come full circle to: generic type Element is private; with function ">"(L,R: Element) return Boolean; package Sortable_Arrays is type Array_Of is array (Integer range <>) of Element; procedure Sort(Arr: in out Array_Of); end Sortable_Arrays; (Add a generic index parameter if you like.) **************************************************************** From: Randy Brukardt Sent: Wednesday, May 11, 2005 5:50 PM ... > Understood. I don't know why this misfeature is required (I must > declare a non-primitive "="), but it is. Probably because it makes no sense to declare a concrete operation for a type with no components or objects. What useful could you possibly write in the body? > Yes, I know that it would > 'cause problems' if a derived type inherited two non-abstract > subprograms with the same signature. I just think that requiring such > subprograms to be overridden--i.e. make them abstract--would be much > more user friendly. > > However, what I was getting at in the example was that I should be able > to (and I can) write subprogram definitions that take parameters of the > interface type, and use attirbutes and subprograms with that notation > (such as Inf'Read) inside the subprogram body. This rule means that I > will have to write 'wrappers' to make the calls look reasonable, but at > least I can have an interface that results in *one* copy of the actual > algorithm. You can write such things, but IMHO they're always wrong. A non-primitive operation on a tagged type should have parameters of the appropriate class-wide type almost all of the time. That should be true in any case where allowing extensions of the type are expected. The exceptions to this rule are for cases like constructors, where extensions are discouraged or (as for function results) impossible. (The notion that 'Class has something to do with dispatching is one of largest dis-services done to Ada 95 users. It's all about extensions; yes, you need something class-wide to get dispatching, but it has many uses that have nothing to do with dispatching.) So, any such operation for interfaces (for which you cannot have constructors) should have 'Class as its arguments. We don't make using specific interfaces illegal because we don't want to deal with the generic contract problems that that would cause, but there is no reason to ever do it. > I can write a package that provides a sort routine through inheritance? Of course. > package Quicksort is > > type Sortable is interface; > > package Ops is > function ">"(L,R: Sortable) return Boolean; > end Ops; This makes no sense. Sortable has no components, so how could you meaningfully write a body for it. It is the users of this interface that have to provide the body. > generic > type Index is (<>); > type Element is private; > type To_Be_Sorted is new Sortable; > with function Indexed_Element(S: To_Be_Sorted; I: Index) return Element; > with procedure Set_Element(To: Element; S: in out To_Be_Sorted; I: Index); > procedure Sort(Arr: in out To_Be_Sorted); > > end Quicksort; So I'd write this package like: package Quicksort is type Sortable is interface; function ">" (Left, Right : Sortable) return Boolean is abstract; generic type Index is (<>); type Element is private; type To_Be_Sorted is new Sortable; with function Indexed_Element(S: To_Be_Sorted; I: Index) return Element; with procedure Set_Element(To: Element; S: in out To_Be_Sorted; I: Index); procedure Sort(Arr: in out To_Be_Sorted); end Quicksort; Except that it doesn't make sense to separate Sortable and the element type. So I'd suggest something like: package Quicksort is type Sortable is interface; function ">" (Left, Right : Sortable) return Boolean is abstract; generic type Index is (<>); type Sortable_Element is new Sortable; with function Indexed_Element(I: Index) return Element; with procedure Set_Element(To: Element; I: Index); procedure Sort; end Quicksort; But I'd really rather make this generic on the array type: package Quicksort is type Sortable is interface; function ">" (Left, Right : Sortable) return Boolean is abstract; generic type Index is (<>); type Sortable_Element is new Sortable; type Sorted_Array is (Index) of Sortable_Element; procedure Sort (Arr: in out Sorted_Array); end Quicksort; But all of these interface solutions suffer from the fundamental failing of Interfaces: you can only have one copy of the interface (and, worse, only a specified name for the routine). So to allow differ sort orders, you'd have to build that into the Sorting procedure, since you can't build it into the interfaces. And you really don't want people to have to define a nonsense ">" just so sorting works (because lots of other stuff would fail). So it would be best to avoid using the name ">" here. And perhaps best to just stick with a generic sort. (Especially since one comes with Ada 2006. :-) **************************************************************** From: Robert I. Eachus Sent: Wednesday, May 11, 2005 7:38 PM >>Understood. I don't know why this misfeature is required (I must >>declare a non-primitive "="), but it is. > >Probably because it makes no sense to declare a concrete operation for a >type with no components or objects. What useful could you possibly write in >the body? An algorithm that 'makes sense' once the (abstract or null) are overridden by operations for the type that inherits from the interface. >You can write such things, but IMHO they're always wrong. A non-primitive >operation on a tagged type should have parameters of the appropriate >class-wide type almost all of the time. That should be true in any case >where allowing extensions of the type are expected. The exceptions to this >rule are for cases like constructors, where extensions are discouraged or >(as for function results) impossible. Or as in one of my examples, where you want to *avoid *tag checks and dispatching. >>package Quicksort is >> >> type Sortable is interface; >> >> package Ops is >> function ">"(L,R: Sortable) return Boolean; >> end Ops; >> >> > >This makes no sense. Sortable has no components, so how could you >meaningfully write a body for it. It is the users of this interface that >have to provide the body... > >So I'd write this package like: > >package Quicksort is > > type Sortable is interface; > function ">" (Left, Right : Sortable) return Boolean is abstract; You are right, that is much better in this case, I was focusing on the situation I described above, where you can have a meaningful body in terms of other primitives. For a non-realistic example: function ">" (Left, Right: Sortable) return Boolean is begin return Right < Left; end ">"; Obviously this would be a bad thing to inherit. I was thinking more in terms of algorithms like Runge-Kutta, Fast Fourier Transform, etc. >But all of these interface solutions suffer from the fundamental failing of >Interfaces: you can only have one copy of the interface (and, worse, only a >specified name for the routine). So to allow differ sort orders, you'd have >to build that into the Sorting procedure, since you can't build it into the >interfaces. And you really don't want people to have to define a nonsense >">" just so sorting works (because lots of other stuff would fail). So it >would be best to avoid using the name ">" here. And perhaps best to just >stick with a generic sort. (Especially since one comes with Ada 2006. :-) I was using Sort as a strawman. One of the problems that I have with Ada today is trying to abstract algorithms from type specific code, so that you don't have to maintain several versions of the same algorithm. Unfortunately generics don't work, because you want Integer, Float, and Fixed versions--and sometimes Unsigned as well. Yes, there are some cases where this is just not possible--code for fixed and floating matrix inversion needs to be very different. But there are a few where you end up with two or more variables and a result that may be any of the three types. (Well, I'm thinking mostly of distribution functions in statistics, the result domain is usually well defined as to type--for a particular distribution. But there are cases where I should allow both float and fixed probabilities.) **************************************************************** From: Randy Brukardt Sent: Wednesday, May 11, 2005 9:09 PM ... > >Probably because it makes no sense to declare a concrete operation for a > >type with no components or objects. What useful could you possibly write in > >the body? > > > > > An algorithm that 'makes sense' once the (abstract or null) are > overridden by operations for the type that inherits from the interface. OK, but to get to the overridden routines, you need dispatching. And to get dispatching, you want the parmeters to your operation to be class-wide. So, I ask again, what useful code could you write in the body of an operation with *specific* interface parameters? ... > Or as in one of my examples, where you want to *avoid *tag checks and > dispatching. If you want to avoid dispatching, you can't use interfaces. They *only* work through dispatching. That's different than regular abstract types, which can have components and thus useful concrete operations. But really, dispatching is so cheap that it is silly to try to avoid it. The only valid reason is the use of tools that don't support it (like SPARK), and there I think the tools should be improved. You'd be better off avoiding generics (at least if you are using Janus/Ada) or discriminants controlling arrays. ... > You are right, that is much better in this case, I was focusing on the > situation I described above, where you can have a meaningful body in > terms of other primitives. For a non-realistic example: > > function ">" (Left, Right: Sortable) return Boolean is > begin return Right < Left; end ">"; This is illegal, since "<" is necessarily abstract (functions can't be null), and thus cannot be called except in a dispatching call. (That makes it even more unrealistic than you had supposed. :-) If you had written: function ">" (Left, Right: Sortable'Class) return Boolean is begin return Right < Left; end ">"; This would work fine, but then we wouldn't be having this conversation. :-) ... > I was using Sort as a strawman. One of the problems that I have with > Ada today is trying to abstract algorithms from type specific code, so > that you don't have to maintain several versions of the same algorithm. > Unfortunately generics don't work, because you want Integer, Float, and > Fixed versions--and sometimes Unsigned as well. Yes, there are some > cases where this is just not possible--code for fixed and floating > matrix inversion needs to be very different. But there are a few where > you end up with two or more variables and a result that may be any of > the three types. (Well, I'm thinking mostly of distribution functions > in statistics, the result domain is usually well defined as to type--for > a particular distribution. But there are cases where I should allow > both float and fixed probabilities.) True enough. There seem to be examples where Interfaces work fine. As long as the operations are well-defined and don't potentially conflict with normal practice. I usually use the queuing example as a strawman, since it is a much better fit for interfaces. **************************************************************** From: Duncan Sands Sent: Thursday, November 17, 2005 10:09 AM We have started using interfaces in our code, using the recent GNAT implementation, and immediately ran into an annoyance: Suppose I want to declare a tagged private type Tagged_Type and specify that it implements some interface Interface_Type. It seems I have to do as follows: package Example is ... type Tagged_Type is new Interface_Type with private; ... private ... type Tagged_Type is new Interface_Type with... ... end; i.e. I am required to derive Tagged_Type from Interface_Type. Contrast this with the following (invented syntax): package Example2 is ... type Tagged_Type is tagged private and Interface_Type; ... private ... type Tagged_Type is new Some_Other_Tagged_Type and Interface_Type with... ... end; In Example2 the intent is simply to state that Tagged_Type implements the interface, but is not necessarily derived from it. There seems to be no way to do this currently. I came across this while building up families of successively more complicated types, analogous to the packages below. I've commented out the declarations I would really like to make, but which aren't provided for in the current draft. Is there any reason to forbid this kind of construction? Full example: package Simple_Interface is type Simple_Interface_Type is interface; procedure Do_Something_Simple (SI : Simple_Interface_Type) is abstract; end; with Simple_Interface; package Complex_Interface is type Complex_Interface_Type is interface and Simple_Interface.Simple_Interface_Type; procedure Do_Something_More_Complicated ( SI : Complex_Interface_Type ) is abstract; end; with Simple_Interface; package Simple_Example is type Simple_Type is new Simple_Interface.Simple_Interface_Type with private; -- forced to derive from Simple_Interface_Type, even though we only want to -- say that we implement it -- type Simple_Type -- is tagged private and Simple_Interface.Simple_Interface_Type; procedure Do_Something_Simple (ST : Simple_Type); private type Simple_Type is new Simple_Interface.Simple_Interface_Type with null record; -- type Simple_Type is Simple_Interface.Simple_Interface_Type with null record; end; with Complex_Interface; private with Simple_Example; package Complex_Example is type Complex_Type is new Complex_Interface.Complex_Interface_Type with private; -- forced to derive from Complex_Interface_Type, even though we only want to -- say that we implement it -- type Complex_Type -- is tagged private and Complex_Interface.Complex_Interface_Type; procedure Do_Something_Simple (CT : Complex_Type); procedure Do_Something_More_Complicated (CT : Complex_Type); private type Complex_Type is new Simple_Example.Simple_Type and Complex_Interface.Complex_Interface_Type with null record; -- illegal, but would be fine with the alternative declarations end; **************************************************************** From: Duncan Sands Sent: Monday, November 21, 2005 1:26 PM > In Example2 the intent is simply to state that Tagged_Type implements > the interface, but is not necessarily derived from it. By this, as I hope was clear, I mean that Tagged_Type has the interface as progenitor or parent, as opposed to having the interface as parent, which is what seems to be the only choice right now. **************************************************************** From: Randy Brukardt Sent: Monday, November 21, 2005 1:51 PM ... > Suppose I want to declare a tagged private type Tagged_Type and specify > that it implements some interface Interface_Type. It seems I have to do > as follows: > > package Example is > ... > type Tagged_Type is new Interface_Type with private; > ... > private > ... > type Tagged_Type is new Interface_Type with... > ... > end; > > i.e. I am required to derive Tagged_Type from Interface_Type. No, that's wrong. The Note 7.3(20) only applies to non-interface types (we're rewording it to fix that). ... > In Example2 the intent is simply to state that Tagged_Type implements > the interface, but is not necessarily derived from it. You're confused (possibly by the broken note). Tagged_Type is "derived from" every interface ancestor of the type, not just the progenitors and parent. (3.4.1(2) defines both "derived directly from" and "derived indirectly from".) "implements an interface" is not a technical term in Ada 2005. > There seems to be no way to do this currently. Yes, there is. The following is legal: package Example3 is ... type Tagged_Type is new Interface_Type with private; ... private ... type Tagged_Type is new Some_Other_Tagged_Type and Interface_Type with... ... end; The rule in question only requires that both types are descended from the same set of interfaces. > I came across this while building up > families of successively more complicated types, analogous to the packages > below. I've commented out the declarations I would really like to > make, but which aren't provided for in the current draft. Is there any > reason to forbid this kind of construction? No, because it isn't forbidden. (Of course, the implementation you are using may have bugs...) **************************************************************** From: Edmond Schonberg Sent: Monday, November 21, 2005 1:53 PM > No, because it isn't forbidden. (Of course, the implementation you > are using may have bugs...) What??? Unheard of.... In fact Pascal and Tucker straightened us out on this point, and this is being fixed as we speak. **************************************************************** From: Adam Beneschan Sent: Monday, November 21, 2005 1:55 PM ... > In Example2 the intent is simply to state that Tagged_Type implements > the interface, but is not necessarily derived from it. [clarified in a later message:] >> By this, as I hope was clear, It wasn't. > There seems to > be no way to do this currently. I came across this while building up > families of successively more complicated types, analogous to the packages > below. I've commented out the declarations I would really like to > make, but which aren't provided for in the current draft. Is there any > reason to forbid this kind of construction? I don't see anything in 7.3 that prohibits this: package Example is type Tagged_Type is new Interface_Type with private; private type Tagged_Type is new Some_Other_Tagged_Type and Interface_Type with...... end Example; I also don't see why it would make a difference that Interface_Type is a parent instead of a progenitor. The syntax defines it as such; however, I'm not aware of any substantial difference this makes. If you have a type derived from interface types: type T1 is I1 and I2 and I3 and I4 with record ... end record; Assuming I1-I4 are all interface types, the language calls I1 the "parent" type and I2, I3, and I4 "progenitors". However, I'm not aware of any real difference---that is, I don't think there's any property of I1 as it pertains to this type that isn't also a property of I2, I3, or I4. I could have missed something on either of these points, and I'm sure someone more knowledgeable than I will point it out. **************************************************************** From: Duncan Sands Sent: Monday, November 21, 2005 2:53 PM Hi Randy, thanks for replying. ... > You're confused (possibly by the broken note). Tagged_Type is "derived from" > every interface ancestor of the type, not just the progenitors and parent. > (3.4.1(2) defines both "derived directly from" and "derived indirectly > from".) "implements an interface" is not a technical term in Ada 2005. Sorry for the terminological sloppiness - I fired off the email in the heat of the moment. ... > No, because it isn't forbidden. (Of course, the implementation you are using > may have bugs...) That is great news, this is indeed what I was looking for. Unfortunately, the implementation I am using rejects the above Example3. Calling this a bug seems a bit mean though, if the version of 7.3(20) that allows Example3 hasn't been written yet... I will send in a bug report none-the-less! **************************************************************** From: Duncan Sands Sent: Monday, November 21, 2005 3:06 PM Hi Adam, > >> By this, as I hope was clear, > > It wasn't. sorry about that: type in haste, repent at leasure. > I don't see anything in 7.3 that prohibits this: > > package Example is > type Tagged_Type is new Interface_Type with private; > private > type Tagged_Type is new Some_Other_Tagged_Type > and Interface_Type with...... > end Example; Well, it seems to be forbidden by the version of 7.3(20) at http://www.adaic.com/standards/rm-amend/html/RM-7-3.html, since "the parent type of the full view can be any descendant of the ancestor type" seems to be trying to say that the parent type of the full view must be a descendant of the ancestor type, which is not the case in the above Example. However Randy Brukardt pointed out in his reply that this is not what was intended. ****************************************************************