!standard 3.4(01) 05-05-05 AI95-00419-01/04 !standard 3.4(02) !standard 3.4(03) !standard 3.4(05) !standard 3.4(15) !standard 3.7(10) !standard 3.9.1(03) !standard 3.9.4(01) !standard 7.3(02) !standard 7.3(06) !standard 7.3(08) !standard 7.3(10) !standard 7.5(02) !standard 7.5(04) !standard 7.5(06) !standard 9.1(9.1) !standard 9.4(11) !standard 12.5.1(03) !class amendment 05-03-09 !status Amendment 200Y 05-03-10 !status ARG Approved 11-0-0 05-04-16 !status work item 05-03-09 !status received 05-03-09 !priority High !difficulty Medium !subject Limitedness of derived types !summary (See proposal.) !problem Draft 10 of the Reference Manual defines the limitedness of a derived type based on the limitedness of its parent. This has the unpleasant consequence that, for a type whose parent is an interface, the parent and the progenitors do not play symmetrical roles, as shown by the following example: type LI is limited interface; type NLI is interface; type T1 is new LI and NLI with ...; -- Limited, illegal. type T2 is new NLI and LI with ...; -- Nonlimited, OK. (The declaration of T1 is actually illegal because NLI is nonlimited, and it can only be implemented by a nonlimited type.) Furthermore, declaring a nonlimited type that is derived from limited interfaces requires the introduction of a dummy interface to get the limitedness right: type LI1 is limited interface; type LI2 is limited interface; type T1 is new LI1 and LI2 with ...; -- Limited, but this -- is not what we want. type Nonlimited is interface; type T2 is Nonlimited and LI1 and LI2 with ...; -- Nonlimited, after -- going through hoops. This is likely to confuse users and to create unnecessary complication in real code. !proposal Do not inherit limitedness from a parent interface. A derived type whose parent is an interface is nonlimited by default. New syntax is provided to make it possible to declare that such a type is limited, thus: type T1 is new LI1 and LI2 with ...; -- Nonlimited. type T2 is limited new LI1 and LI2 with ...; -- Limited. There is one additional difficulty due to the fact that an interface type can be passed as an actual for a formal abstract type. For instance: generic type A is abstract limited tagged private; package G is type T is new A with ...; end G; In the generic, type T is surely limited. However, in an instance, the actual type for A could be an interface. Seen from outside the instance, type T would then be nonlimited. There must be a way for the author of the generic to ensure that T is limited. This means that we must allow the following: generic type A is abstract limited tagged private; package G is type T is limited new A with ...; end G; Given that we have to allow this syntax in this particular situation, we might as well allow it uniformly for every derived type: it could be used as a documentation. Although this is optional syntax, and we don't like optional syntax too much, one could argue that it should have been that way from day one as the implicit nature of limitedness is hiding an important property. We also note that derived types are undefined in the Standard; there is no wording saying that a derived type comes from a derived_type_declaration. Similarly, various types that aren't derived types need to have wording to trigger the inheritance rules. !wording Change 3.4(1) as amended by AI95-00401 to read: A derived_type_definition defines a {*derived} type{*} (and its first subtype) whose characteristics are derived from those of a parent type, and possibly from progenitor types. Change 3.4(2) as amended by AI95-00251 to read (note: interface_list is moved): derived_type_definition ::= [abstract] [limited] new parent_subtype_indication [[and interface_list] record_extension_part] Change 3.4(3) as amended by AI95-00251 and AI95-00401 to read: The parent_subtype_indication defines the *parent subtype*; its type is the *parent type*. The interface_list defines the progenitor types (see 3.9.4). A derived type has one parent type and zero or more {progenitor} types. Add after 3.4(5) If the reserved word *limited* appears in a derived_type_definition, the parent type shall be a limited type. AARM Reason: We allow *limited* because we don't inherit limitedness from interfaces, so we must have a way to derive a limited type from interfaces. The word *limited* has to be legal when the parent *could be* an interface, and that includes generic formal abstract types. Since we have to allow it in this case, we might as well allow it everywhere as documentation, to make it explicit that the type is limited. However, we do not want to allow *limited* when the parent is nonlimited: limitedness cannot change in a derivation tree. End AARM. Delete 3.4(15) (this is defined in 7.5). Add the following to the syntax in the new subclause 3.9.4: interface_list ::= interface_subtype_mark {and interface_subtype_mark} Change 3.7(10) (using the terminology introduced by AI-318-2): A discriminant_specification for an access discriminant shall appear only in the declaration for a task or protected type, or for a type {that is a descendamt of an explicitly limited record type} [with the reserved word limited in its [(full)] definition or in that of one of its ancestors]. Change 3.9.1(3): ... If the [parent type]{record extension} is nonlimited, ... Add the following at the end of the Static Semantics of the new subclause 3.9.4: An interface_subtype_mark in an interface_list names a *progenitor subtype*; its type is the *progenitor type*. An interface type inherits user-defined primitive subprograms from each progenitor type in the same way that a derived type inherits user-defined primitive subprograms from its progenitor types (see 3.4). Change 7.3(3) as amended by AI95-0251 to read: private_extension_declaration ::= type defining_identifier [discriminant_part] is [abstract] [limited] new ancestor_subtype_indication [and interface_list] with private; Change the first sentence of 7.3(6) to read: Redundant[A private type is limited if its declaration includes the reserved word *limited*; a private extension is limited if its ancestor type is a limited type that is not an interface type, or if the reserved word *limited* appears in its definition.] Add after 7.3(8): If the reserved word *limited* appears in a private_extension_declaration, the ancestor type shall be a limited type. Add after 7.3(10): If the full view of a private extension is limited, then the reserved word *limited* shall appear in the full_type_declaration if and only if it also appears in the private_extension_declaration. AARM Reason: The word *limited* is optional (unless the ancestor is an interface), but if you use it, do so consistently. Otherwise things would be too confusing for the reader. Change 7.5(2) to read: If a tagged record type has any limited components, then the reserved word *limited* shall appear in its record_type_definition. If the reserved word *limited* appears in the definition of a type extension, its parent type Redundant[and any progenitor interfaces] shall be limited. Change 7.5(3-6) to read: (This includes the change of AI-411): A type is limited if it is one of the following: * A type with the reserved words *limited*, *synchronized*, *task* or *protected* in its definition; * A composite type with a limited component; * A derived type whose parent is limited and is not an interface. AARM Reason: We considered a rule where limitedness was always inherited from the parent for derived types, but in the case of a type whose parent is an interface, this meant that the first interface is treated differently than other interfaces. It also would have forced users to declare dummy nonlimited interfaces just to get the limitedness right. We also considered a syntax like *not limited* to specify nonlimitedness when the parent was limited, but that was unsavory. The rule is more uniform and simpler to understand. The rules for interfaces are unsymmetrical, but the language is not: if the parent interface is limited, the presence of the word *limited* determines the limitedness, and nonlimited progenitors are illegal by the rules in 3.9.4. If the parent interface is nonlimited, the word *limited* is illegal by the rules in 3.4. The net effect is that the order of the interfaces doesn't matter. End AARM. Modify the first paragraph added after 9.1(9.1) by AI-345 as follows: For a task_type_declaration {with an interface_list, the task type inherits user-defined primitive subprograms from each progenitor type (see 3.9.4), in the same way that a derived type inherits user-defined primitive subprograms from its progenitor types (see 3.4).} [i]{I}f 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 entry_declaration for a single entry with the same identifier within the task_type_declaration, whose profile is type conformant with the prefixed view profile of inherited subprogram, the inherited subprogram is said to be implemented by the conforming task entry. Modify the first paragraph added after 9.1(11) by AI-345 as follows: For a protected_type_declaration {with an interface_list, the protected type inherits user-defined primitive subprograms from each progenitor type (see 3.9.4), in the same way that a derived type inherits user-defined primitive subprograms from its progenitor types (see 3.4).} [i]{I}f 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 protected_operation_declaration for a protected subprogram or single entry with the same identifier within the protected_type_declaration, whose profile is type conformant with the prefixed view profile of the inherited subprogram, the inherited subprogram is said to be implemented by the conforming protected subprogram or entry. Change 12.5.1(3) as amended by AI95-00251 to read: formal_derived_type_definition ::= [abstract] [limited] new subtype_mark [[and interface_list] with private] !discussion (See proposal.) !example (See proposal.) !corrigendum 3.4(01) !comment This includes the changes of AI-00401. @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 @i (and its first subtype) whose characteristics are derived from those of a parent type, and possibly from progenitor types. !corrigendum 3.4(02) @drepl @xcode<@fa@ft<@b>@fa<] >@ft<@b @i>@fa> @dby @xcode<@fa@ft<@i>@fa@ft<@b>@fa< >@ft<@i>@fa> @xcode<@fa@ft<@b>@fa<] [>@ft<@b>@fa<] >@ft<@b @i>@fa@ft<@b>@fa< interface_list] record_extension_part]>> !corrigendum 3.4(03) @drepl The @i@fa defines the @i; its type is the parent type. @dby The @i@fa 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) @dinsa 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. @dinst If the reserved word @b appears in a @fa, the parent type shall be a limited type. !corrigendum 3.4(15) @ddel @xbullet !corrigendum 3.7(10) @drepl A @fa for an access discriminant shall appear only in the declaration for a task or protected type, or for a type with the reserved word @b in its (full) definition or in that of one of its ancestors. In addition to the places where Legality Rules normally apply (see 12.3), this rule applies also in the private part of an instance of a generic unit. @dby A @fa for an access discriminant shall appear only in the declaration for a task or protected type, or for a type that is a descendant of an explicitly limited record type. In addition to the places where Legality Rules normally apply (see 12.3), this rule applies also in the private part of an instance of a generic unit. !corrigendum 3.9.1(03) @drepl The parent type of a record extension shall not be a class-wide type. If the parent type is nonlimited, then each of the components of the @fa shall be nonlimited. The accessibility level (see 3.10.2) of a record extension shall not be statically deeper than that of its parent type. In addition to the places where Legality Rules normally apply (see 12.3), these rules apply also in the private part of an instance of a generic unit. @dby The parent type of a record extension shall not be a class-wide type. If the record extension is nonlimited, then each of the components of the @fa shall be nonlimited. The accessibility level (see 3.10.2) of a record extension shall not be statically deeper than that of its parent type. In addition to the places where Legality Rules normally apply (see 12.3), these rules apply also in the private part of an instance of a generic unit. !corrigendum 3.9.4(01) !comment Dummy to cause a conflict. @drepl @xcode<@fa@ft<@i>@fa@ft<@b>@fa< >@ft<@i>@fa> @dby @xcode<@fa@ft<@i>@fa@ft<@b>@fa< >@ft<@i>@fa> !corrigendum 7.3(02) @drepl @xcode<@fa@ft<@b>@fa< defining_identifier [discriminant_part] >@ft<@b>@fa< [>@ft<@b>@fa<] >@ft<@b>@fa< ancestor_subtype_indication >@ft<@b>@fa<;>> @dby @xcode<@fa@ft<@b>@fa< defining_identifier [discriminant_part] >@ft<@b>@fa< [>@ft<@b>@fa<] [>@ft<@b>@fa<] >@ft<@b>@fa< ancestor_subtype_indication [>@ft<@b>@fa< interface_list] >@ft<@b>@fa<;>> !corrigendum 7.3(06) @drepl A private type is limited if its declaration includes the reserved word @b; a private extension is limited if its ancestor type is limited. If the partial view is nonlimited, then the full view shall be nonlimited. If a tagged partial view is limited, then the full view shall be limited. On the other hand, if an untagged partial view is limited, the full view may be limited or nonlimited. @dby A private type is limited if its declaration includes the reserved word @b; a private extension is limited if its ancestor type is a limited type that is not an interface type, or if the reserved word @b appears in its definition. If the partial view is nonlimited, then the full view shall be nonlimited. If a tagged partial view is limited, then the full view shall be limited. On the other hand, if an untagged partial view is limited, the full view may be limited or nonlimited. !corrigendum 7.3(08) @dinsa The @i of a @fa is the subtype defined by the @fa; the ancestor type shall be a specific tagged type. The full view of a private extension shall be derived (directly or indirectly) from the ancestor type. In addition to the places where Legality Rules normally apply (see 12.3), the requirement that the ancestor be specific applies also in the private part of an instance of a generic unit. @dinst If the reserved word @b appears in a @fa, the ancestor type shall be a limited type. !corrigendum 7.3(10) @dinsa If a private extension inherits known discriminants from the ancestor subtype, then the full view shall also inherit its discriminants from the ancestor subtype, and the parent subtype of the full view shall be constrained if and only if the ancestor subtype is constrained. @dinst If the full view of a private extension is limited, then the reserved word @b shall appear in the @fa if and only if it also appears in the @fa. !corrigendum 7.5(02) @drepl If a tagged record type has any limited components, then the reserved word @b shall appear in its @fa. @dby If a tagged record type has any limited components, then the reserved word @b shall appear in its @fa. If the reserved word @b appears in the definition of a type extension, its parent type and any progenitor interfaces shall be limited. !corrigendum 7.5(03) @drepl A type is @i if it is a descendant of one of the following: @dby A type is @i if it is one of the following: !corrigendum 7.5(04) !Comment note that the rewrite of this list eliminates the AI-411 change. @drepl @xbullet in its definition;> @dby @xbullet, @b, @b, or @b in its definition;> !corrigendum 7.5(05) @ddel @xbullet !corrigendum 7.5(06) @drepl @xbullet @dinst @xbullet @xbullet !corrigendum 9.1(9.1) !comment Dummy to force conflict; actual change is in the conflict file. @drepl Dummy. @dby Dummy. !corrigendum 9.4(11) !comment Dummy to force conflict; actual change is in the conflict file. @drepl Dummy. @dby Dummy. !corrigendum 12.5.1(03) @drepl @xcode<@fa@ft<@b>@fa<] >@ft<@b>@fa< subtype_mark [>@ft<@b>@fa<]>> @dby @xcode<@fa@ft<@b>@fa<] [>@ft<@b>@fa<] >@ft<@b>@fa< subtype_mark [[>@ft<@b>@fa< interface_list] >@ft<@b>@fa<]>> !ACATS test ACATS tests should be constructed to test the legality rules here. !appendix From: Tucker Taft Sent: Thursday, February 17, 2005 3:31 AM I had a grab-bag of morning after thoughts after the Paris ARG meeting. (Unfortunatly, that meant I didn't get much sleep on the overnight train to Tignes. ;-) I'll mention just the first two in this e-mail, since I am running out of time before I have to zip off to the airport. ----------------- 1) There seems no reason we can't "hide" the fact that a type implements an interface, so long as the interface has no user-defined primitive subprograms. In some ways, this is analogous to hiding the fact that a type is nonlimited, if we think of "nonlimitedness" as being an interface with ":=" and "=". 2) I am concerned about the rule that if you have a type that should be nonlimited, but also needs to implement one or more limited interfaces, that you have to concoct an "artificial" nonlimited parent type or interface. One possibility that popped into my head was: type T is not limited and Int1 and Int2 with ... This way the type can implement some limited interfaces without having to specify a nonlimited parent. This syntax also suggests a similar solution for partial views of tasks and protected types that implement interfaces: type TT is task and Int1 and Int2 with private; type PT is protected and Int1 and Int2 with private; And for completeness, and also to make the fact that a type is "limited" clear: type LT is limited and Int1 and Int2 with ... and at which point we might as well allow type ST is synchronized and Int1 and Int2 with private; Hence the overall syntax would be approximately: type [] is and with ... ::= [not] limited | task | protected | synchronized This syntax would only be permitted when there is an interface_list, as a way to specify the kind of the type without having to rely on there being an interface that has that same kind. My biggest fear is that there will be a proliferation of silly interfaces just to provide parent types when wanting to make a type nonlimited, or indicate that a generic formal type is a task, etc. Having a proliferation of these will make integration across independently developed subsystems that much harder, and is worse than the similar situation with access types, since we have said there may be no hidden interfaces. Note that idea (1) above is a partial solution to this problem, but I still think the proliferation will be nasty. I also think the added documentation of specifying "not limited" or "limited" explicitly will be an advantage. More later ;-) **************************************************************** From: Pascal Leroy Sent: Thursday, February 17, 2005 7:59 AM > 1) There seems no reason we can't "hide" the > fact that a type implements an interface, so long > as the interface has no user-defined primitive > subprograms. In some ways, this is analogous to > hiding the fact that a type is nonlimited, if > we think of "nonlimitedness" as being an interface > with ":=" and "=". Hmm. I wouldn't be so optimistic. In the case of nonlimited interfaces, it seems to me that at a minimum you would not want to hide the nonlimitedness. In other words, the following should be illegal: type I is interface; package P is type T is tagged limited private; private type T is new I with null record; end P; If it is not illegal, clients of P can derive from P.T and redefine "=", and we would end up with the same situation as described in the AARM note of AI 396, where the client ends redefine an operation that they don't know about, or where the type ends up with two distinct "=" operators. In the case of limited interfaces, you have to deal with the compatibility rules between task and protected interfaces. For example: type TI is task interface package P is type T is tagged limited private; private type T is new TI with null record; end P; type PI is protected interface; type T2 is new P.T with PI and null record; -- Legal One hope that the declaration of type T2 is illegal, because there is no way that a type can implement both a task interface and a protected interface, but of course this leads to a privacy violation. I suppose the idea could be made to work with appropriate restrictions, but it's probably not worth the effort. I for one cannot get excited about interfaces which have no user-defined subprograms. **************************************************************** From: Randy Brukardt Sent: Thursday, February 17, 2005 11:30 AM I would go further and say that a type with no subprograms and no components is next to useless. The mostly likely place for them to show up would be prototyping, and you wouldn't be doing anyone any favors by allowing them in places that would become illegal when the subprograms are later added to the interface. So I suggest that Tuck get some sleep rather than worrying about these ideas. :-) **************************************************************** From: Tucker Taft Sent: Thursday, February 17, 2005 4:06 PM These are used pretty heavily in Java, as a run-time testable property of a type. E.g. Cloneable, Serializable, EventListener. They are called "marker" interfaces, in one article I found. I think they might turn out to serve a similar purpose in Ada, and if so, we wouldn't want to place unnecessary requirements on them such as requiring them to be visible in all cases. **************************************************************** From: Randy Brukardt Sent: Thursday, February 17, 2005 5:59 PM I don't see how the latter follows the former. I'm strongly opposed to rules that depend on the shapes of types, whether there are components or interfaces, the phase of the moon, or anything else that is easy to change. Those are maintenance bombs waiting to happen. I still oppose AI-391 (not sure why I didn't vote against it), and I'll oppose this, too. The model of interfaces is that it doesn't matter where in the hierarchy they are added. That makes them fundamentally privacy-breaking, and the rule that requires them to be visible for tagged types eliminates problems. If we go away from that, we'll have to revisit lots of things (like the Interface_Ancestor_Tags function we added last week), because they wouldn't be meaningful. I also do not see how this could work in Ada. (I don't see how it could work in any language, for that matter, that claims to have strong typing.) The only possible thing that you could do is test for membership, as there are no operations to dispatch to. (That's already very dubious from an O-O perspective, but let's not go there.) You can't even say "Foobar in Serializable", because both operands have to be class-wide before you are even allowed to combine them. You would have to say "Foobar in Serializable'Class", which is bizarre, since there would never be an interface descended from Serializable for this use. And once that proved to be true, what could you do? Absolutely nothing, unless you use this to assume some implementation in terms of predefined operations. "Serializable" is an example; presumably it means that the type has 'Read and 'Write; but the language already handles that in a variety of ways. A further marker isn't needed in this particular case. I certainly see no reason to copy hair-brained ideas from Java; Ada can (and does) do this stuff so much better. If all we're trying to do is clone Java, count me out. **************************************************************** From: Pascal Leroy Sent: Friday, February 18, 2005 3:27 AM Tuck wrote: > These are used pretty heavily in Java, as a run-time testable > property of a type. E.g. Cloneable, Serializable, > EventListener. They are called "marker" interfaces, in one > article I found. I see this as poor design on the part of the Java folks, and I don't understand why we would want to replicate it in Ada. Take for instance Cloneable. The Java hierarchy is such that Object (the root of everything) has a clone method. But for some classes clone doesn't make sense. So they add a class Cloneable that is just a marker (I have seen this called a tagging interface, too), and the method clone raises an exception for all classes that don't implement Cloneable, and no others. Yuck. Someone was terribly confused when they decided to provide clone at the Object level. Note that .Net has a structure that I find much cleaner: the interface ICloneable has a single method, Clone, and you inherit Clone iff you implement ICloneable. No cheesy exceptions, membership tests, etc. Serializable has similar semantics. I didn't check the others, but it certainly seems like this "tagging" mechanism is widely (mis)used in Java. Of course, in Ada Cloneable is spelled "nonlimited" and Serializable is spelled "stream-oriented attributes are available" so for these two particular interfaces we have support hard-wired in the language. **************************************************************** From: Jean-Pierre Rosen Sent: Friday, February 18, 2005 4:03 AM [...] I strongly support that view. I always claimed that this use of interfaces was a kludge, used only for "magic" interfaces that were recognized specially by the language. **************************************************************** From: Pascal Leroy Sent: Thursday, February 17, 2005 8:15 AM > 2) I am concerned about the rule that if you > have a type that should be nonlimited, but also > needs to implement one or more limited interfaces, > that you have to concoct an "artificial" nonlimited > parent type or interface. ... We discussed something like that in the past, and one Tucker Taft was not exactly enthusiastic. See the mail dated October 29 in AI 345. I am not at all opposed to a syntax like the above, as it's clear that making the first interface "special" is somewhat unpleasant. However, I am going to insist that whatever syntax we choose include the word "new" somewhere, as we are really writing type derivations. We want to make sure that the syntax clearly distinguishes an interface declaration like: type I is I1 and I2 and I3; from a "normal type" declaration like: type T is new I1 and I2 and I3 with ...; When I look at the syntax above, it's not clear to me if you are declaring bona fide types or mere interfaces. Oh, and I hate to mention this, but it's awfully late. At this point the only part of the core language that you should be changing is AI 416... **************************************************************** From: Robert Eachus Sent: Thursday, February 17, 2005 3:51 PM I agree that it is almost certainly too late to entertain a change in the syntax of this magnitude. However, I think that a legality rule should fix the problem. If a type which inherits interfaces is not declared limited, it is not limited. Add that if a rule that says that a type which inherits from an interface which must be limited (such as a synchronized interface) must be declared limited. Now in the example above, type T is limited and Int1 and Int2 with... is a limited type, and type T is Int1 and Int2 with... *if legal*, is a non-limited type. Now you get down to the real issue, when can an interface declared as limited be inherited by a non-limited type? I think the rule has to be that if public interface is a task, protected or synchronized interface, a non-limited type or interface can't inherit it. It probably makes sense in that case to say that interfaces derived from task, protected, or synchronized interfaces must either be declared to be of the same class or (the more general) synchronized. So why have limited interfaces in the first place? Because there are cases where you want to declare an interface that doesn't require assignment and equality. There is no reason to prevent such interfaces from being inherited by non-limited types. But the task, protected, and synchronized interface declarations make no sense if implemented by a type with copying permitted. **************************************************************** From: Tucker Taft Sent: Thursday, February 17, 2005 4:13 PM > We discussed something like that in the past, and one Tucker Taft was not > exactly enthusiastic. See the mail dated October 29 in AI 345. I admit my view has changed (that's why it is called "morning after" ;-). In examples, I have been bumping into this requirement to have a "bogus" parent type just to avoid having the new type end up limited if it wants to implement a limited interface. > > I am not at all opposed to a syntax like the above, as it's clear that > making the first interface "special" is somewhat unpleasant. However, I > am going to insist that whatever syntax we choose include the word "new" > somewhere, as we are really writing type derivations. I guess I was somewhat attracted to the idea these were *not* like type derivations, in that they aren't inheriting code or components from anything. They are simply "promising" to implement one or more interfaces. These types would *not* have a parent type, though they would have progenitor interfaces, and hence a set of interface ancestors. > ... We want to make > sure that the syntax clearly distinguishes an interface declaration like: > > type I is I1 and I2 and I3; That is not a legal interface declaration. Interface declarations always need the word "interface": type I is interface and I1 and I2 and I3; > > from a "normal type" declaration like: > > type T is new I1 and I2 and I3 with ...; > > When I look at the syntax above, it's not clear to me if you are declaring > bona fide types or mere interfaces. All interface type declarations have the word "interface," so I don't see the possible confusion. I think it would be worse to have the word "new" but have no parent type specified immediately thereafter. > > Oh, and I hate to mention this, but it's awfully late. At this point the > only part of the core language that you should be changing is AI 416... The morning after is always a bit late... **************************************************************** From: Randy Brukardt Sent: Thursday, February 17, 2005 6:07 PM > I guess I was somewhat attracted to the idea these were *not* > like type derivations, in that they aren't inheriting code or components > from anything. They are simply "promising" to implement one or > more interfaces. These types would *not* have a parent type, though > they would have progenitor interfaces, and hence a set of > interface ancestors. I had suggested something like this well back at the start. I was told I was confused. I do not look kindly on being told I was right all along, but we should invent some totally new syntax to handle this. I thought then that interface ancestors needed to be added to root tagged record declarations (like we eventually did for tasks and protected types). Something like: type Rec is tagged limited record {I1 and} Comp1 : ... end record; If we're going to declare types with no parents, they ought to look like them. And I see no reason whatsoever to invent a new kind of type declaration (as Tucker did) to support that. But make no mistake about it -- if we even think about changing this syntax, we'll have to delay the completion of the Amendment by at least one meeting. And that would substantially increase the likelyhood of running out of money and effort. It had better be *important* in order to do that. (If we were to make this change, we would have to reread the entire AARM looking for places where a parent is assumed if there are any progenitors. That's going to be a huge job.) **************************************************************** From: Tucker Taft Sent: Thursday, February 17, 2005 6:49 PM I believe task and protected types that implement interfaces already have this situation, for what it's worth. **************************************************************** From: Pascal Leroy Sent: Friday, February 18, 2005 3:08 AM > > type I is I1 and I2 and I3; > > That is not a legal interface declaration. Interface > declarations always need the word "interface": > > type I is interface and I1 and I2 and I3; Yeah, I can never get the syntax right. We changed it so many times... > type T is not limited and Int1 and Int2 with ... > type TT is task and Int1 and Int2 with private; > type PT is protected and Int1 and Int2 with private; > > All interface type declarations have the word "interface," > so I don't see the possible confusion. I think it would > be worse to have the word "new" but have no parent type > specified immediately thereafter. From the perspective of syntax, it seems clear to me that it ought to mimic the syntax for full type declarations, with the task/protected definition replaced by the word private. Thus: task type TT is new Int1 and Int2 with private; protected type PT is new Int1 and Int2 with private; Having two unrelated syntax for the partial view and the full view would just be too confusing. The situation is a bit less clear for the "not limited" case, but my preference would go to: type T is not limited new Int1 and Int2 with private; Anyway, the syntax is, as usual, not the big problem here. The impact on the overall (static) semantics of the language is daunting. You'd have to define the semantics of "partial task-ish" and "partial protected-ish" views. And you'd have to decide what to do with generics: it would seem reasonable to define new kinds of formal types corresponding to the partial views above, with appropriate matching rules, and a definition of what you can do with the formal type inside the generic. At first sight, this looks like an earth-shattering change to me. But if you really feel strongly about it you can always try to put together an AI. Maybe we'll discover that it's not so bad after all. (I have my doubts.) **************************************************************** From: Tucker Taft Sent: Friday, February 18, 2005 5:45 AM I'll write an AI, if I continue to have enthusiasm for this. Note that we allow *adding* an interface_list to task, protected, and interface type definitions, without forcing the use of "new". What I am proposing is to add the ability to include an interface_list in record definitions and private type definitions. Then any type that is non-array composite would allow an interface_list, including derived types of course. And if Randy finds it familiar, I hope that isn't a bad thing. Two big changes that occurred during the evolution of the interface feature were: 1) non-limited types could implement limited interfaces; 2) task and protected type declarations allow an interface list. I'm not sure we went back after making these two changes and reconsidered the original tight connection between the derived type syntax and interface_list. We clearly want to allow interfaces as parents of derived types, so one can change an abstract non-interface type into an interface type with minimum disruption. However, one may similarly want to add interfaces to a record type or a private type, without taking on the other requirements of a derived type (i.e. a parent), and that seems to me what is missing. As far as two different syntaxes for partial view tasks/protected and full view tasks/protected, we already have that if you declare a derived type whose parent is a task, protected, or synchronized interface. It seems odd that you can get a partial view task only if you name a task interface, but not if you want to be more explicit about it. Anyway, I'll dump all these arguments into a !discussion section if I find the energy to write up an AI. **************************************************************** From: Tucker Taft Sent: Friday, February 18, 2005 8:05 AM Well, I don't know what version of Chapter 9 my evil twin was reading, but clearly "new" is there, as clear as day, in protected and task types that implement interfaces. That certainly weakens my argument about leaving out "new" for the others. This AI may not see the light of day... **************************************************************** From: Randy Brukardt Sent: Friday, February 18, 2005 10:36 AM Humm; OK by me, but it leaves the original problem unaddressed. That is, that deriving a non-limited type from a limited interface (which is likely to be the most common kind) is messy. You pretty much have to put a dummy non-limited interface around, which hardly is going to increase the clarity of programs. The problem seems limited (see below) to root record types, so that seems to be the only thing that needs addressing. Let's leave "new" in the syntax. Perhaps we need to add that to root record types: type Rec is tagged new I1 and I2 record which would be a good thing if I1 and I2 were limited. The grammar would be: record_type_definition ::= [[abstract] tagged [new interface_list]] [limited] record_definition (The idea is that the interface list associates with "tagged", same as "abstract".) That would be pretty consistent with task and protected types (I dropped "with" here, because it doesn't make sense before "record", and this doesn't belong after "record"). I'm not sure that this is a good idea, but if you are going to pursue something, I think it should be on this line. I have a lot harder time getting excited about partial views of task or protected types, because I think that the interface will naturally have one of the right classes in most interesting cases. Nonlimited isn't allowed, and all of the others seem fine to specify a partial view. **************************************************************** From: Tucker Taft Sent: Saturday, February 19, 2005 7:48 AM I think you may have the same problem with non-limited private types/extensions, where you are required to make the limitedness of the partial view match the limitedness of the full view. Your idea of using "tagged" as a stand-in for "non-limited" is intriguing, though it is a bit subtle. Another possibility is to define non-terminals "parent_subtype_indication" and "ancestor_subtype_indication" used by derived_type_definition and private_type_extension as follows: parent_subtype_indication ::= subtype_indication | [not] limited ancestor_subtype_indication := subtype_indication | [not] limited | task | protected | synchronized and then say that when the parent_subtype_indication is given by a type "category" rather than a subtype_indication, the parent type is an anonymous operation-less interface type of the appropriate category. This way we still have a parent type, albeit anonymous, which has exactly the desired characteristics. You would be allowed to have a "hidden" anonymous interface ancestor of course, since we have other rules that determine when limitness, nonlimitedness, etc., need to be revealed. I might be tempted to call these anonymous interfaces the "root" interfaces, but that might create some wording headaches (or heartaches? ;-). Here are examples of using this anonymous interface parent syntax. I presume we would not allow this notation in the absence of an interface_list (and that could probably be expressed in the BNF). type NT is new not limited and Int1 with private; type TT is new task and Int2 with private; type NR is new not limited and Int3 with record ... end record; type NNR is new not limited and Int4 with null record; Following Randy's suggestion, substituting "tagged" for "not limited" is a possible alternative, though I fear that might be confusing, albeit a bit pleasanter grammatically. > I have a lot harder time getting excited about partial views of task or > protected types, because I think that the interface will naturally have one > of the right classes in most interesting cases. Nonlimited isn't allowed, > and all of the others seem fine to specify a partial view. Yes, I agree this is less important, though I do suspect we will see the creation of operation-less task interfaces, which could create annoyances when integrating independently developed code. **************************************************************** From: Tucker Taft Sent: Thursday, March 3, 2005 4:46 PM The more I think about this problem, the more it seems that adding syntax to solve this problem is overkill. Adding one additional library package with just a "bit" of magic would seem to solve the problem: package Ada.Root_Interfaces is type Root_Limited is limited interface; type Root_Nonlimited is interface and Root_Limited; type Root_Synchronized is synchronized interface and Root_Limited; type Root_Task is task interface and Root_Synchronized; type Root_Protected is protected interface and Root_Synchronized; end Ada.Root_Interfaces; All tagged types and interfaces implicitly implement Root_Limited. All non-limited tagged types and interfaces implicitly implement Root_Nonlimited. All synchronized tagged types and interfaces implicitly implement Root_Synchronized. All task interfaces implicitly implement Root_Task, as do all tagged task types (i.e. those that implement at least one interface). All protected interfaces implicitly implement Root_Protected, as do all tagged protected types (i.e. those that implement at least one interface). Then any time you want to define a non-limited type that also implements a limited interface: type T is new Ada.Root_Interfaces.Root_Nonlimited and lim_intf with ... A partial view of a tagged task type becomes: type TT is new Ada.Root_Interfaces.Root_Task with private; I believe I suggested something similar to this in the past, but it was more of a "nice to have." Now with the ability to have non-limited types implementing limited interfaces, and the general ability to have partial views of task types presuming they implement some interface, it seems important to have some way to specify an interface of a particular sort without having to create one just for the purpose. This seems preferable to new syntax... **************************************************************** From: Pascal Leroy Sent: Friday, March 4, 2005 3:13 AM You have indeed proposed exactly the same idea not so long ago. See the mail dated October 26, 2004 in AI 345. Surely no new technical information has surfaced since then. In particular, it was already possible for non-limited types to implement limited interfaces. -- I think that all the arguments that have been voiced at the time against this idea are still valid. I will merely point out that this is a hybrid approach, and I find that particularly distasteful. Long ago we decided to use syntax to declare interfaces; thus: type TI is task interface; as opposed to using magic types, which would have looked like: type TI is interface and Ada.Root_Interfaces.Root_Task; I believe that using syntax was the right approach, because it improves legibility and avoids the confusion that results from having a single root for the entire inheritance hierarchy. But now you are proposing to mix up the two approaches, so the two declarations below would be equivalent: type I2 is interface and Ada.Root_Interfaces.Root_Task and I1; type I2 is task interface and I1; This doesn't make sense to me. There must be only one way to declare interface I2, not two ways based on whether you prefer syntax or magic interfaces. So if we go for magic interfaces, we must get rid of the syntax for declaring limited/synchronized/task/protected interfaces. -- One thing that I dislike with the rules as they currently are is that the order of the interfaces matters (and your proposal doesn't change that). Consider: type LI is limited interface; type NLI is interface; type T1 is new LI and NLI with null record; -- limited type T2 is new NLI and LI with null record; -- nonlimited The fact that T1 and T2 differ wrt limitedness is confusing and I wonder if we should try to change this. Of course, in the case where the parent is a non-interface type, the limitedness has to be inherited from the parent. But in the case where the parent is an interface type, I am not sure if the rule we have is ideal. I might try to write an AI to fix this. **************************************************************** From: Robert I. Eachus Sent: Satursday, March 5, 2005 5:00 PM Pascal Leroy wrote: > type T1 is new LI and NLI with null record; -- limited > type T2 is new NLI and LI with null record; -- nonlimited > >The fact that T1 and T2 differ wrt limitedness is confusing and I wonder >if we should try to change this. Of course, in the case where the parent >is a non-interface type, the limitedness has to be inherited from the >parent. But in the case where the parent is an interface type, I am not >sure if the rule we have is ideal. I might try to write an AI to fix >this. I think it is extremely confusing, which is why I proposed that a type that inherits only from interfaces must contain the reserved word 'limited' to be a limited type: *type* T1 *is limited new* LI *and* NLI *with null record*; -- limited *type* T2 *is new* LI *and* NLI *with null record*; -- nonlimited The only problem with this idea, and I am not sure it is a problem, is when such a type must be limited because one of the parent interfaces is synchronized or a task interface. Favoring readers over writers in that situation argues for a legality rule. Yes, the writer may be a bit exasperated as he goes back to add the (otherwise unnecessary) 'limited.' but readers would sure find it useful. This could be extended to allow/require: *type* T2* is not limited **new *L1* and *NLI* with null record; * I my opinion that would be extreme overkill. In Ada the rule is that a type is non-limited, unless it is derived from a limited type, is a task or protected type, or is defined to be limited. Let's keep it that way. Incidentally I am not at all sure that it is necessary that types that implement synchronized interfaces must be limited. But I don't see any real reason to open that can of worms in the declaration. Certainly there will be cases where in the body of the package that implements a synchronized type copying objects of the type may be necessary. But in those cases I expect that the visible type will be a record containing an indirection pointer.* * **************************************************************** From: Tucker Taft Sent: Saturday, March 5, 2005 7:30 AM Pascal Leroy wrote: > One thing that I dislike with the rules as they currently are is that the > order of the interfaces matters (and your proposal doesn't change that). > Consider: > > type LI is limited interface; > type NLI is interface; > > type T1 is new LI and NLI with null record; -- limited > type T2 is new NLI and LI with null record; -- nonlimited > > The fact that T1 and T2 differ wrt limitedness is confusing and I wonder > if we should try to change this. Of course, in the case where the parent > is a non-interface type, the limitedness has to be inherited from the > parent. But in the case where the parent is an interface type, I am not > sure if the rule we have is ideal. I might try to write an AI to fix > this. Ok, I'm convinced. Not to preempt you, but combining various suggestions from you, Randy, Robert Eachus, etc., I would support the following: type NT is [abstract] [limited] new I1 and I2 and I3 with ... Limitness may be asserted on a derived type, in the same way it may be asserted on a record type. Limitness is *not* inherited from a parent or progenitor that is a limited, non-synchronized interface, so limitness *must* be asserted if the parent and progenitors are all limited, non-synchronized interfaces, and the programmer wants the derived type to be limited. We could go further and say that limitness is *not* inherited from interfaces, period. That would mean that "limited new" is *required* if the parent or any of the progenitors are synchronized. I suppose that would be consistent with the current rule for tagged limited record types, where you have to say limited explicitly even if there is a task or protected component. It would also be consistent with our rule on defining interfaces, where you must respecify limited (or some other qualifier like "synchronized") always, independent of the progenitors. I would allow limitedness to be asserted when it *is* inherited, so that you can change between an interface and a tagged type during maintenance, and not have to remove "limited" from the places where it is now inherited. This would be consistent with untagged records, where we allow "limited" even when it is redundant. I suppose we could go further and allow "limited" to be asserted on an untagged derived type, and in that way support a coding convention where limited is always re-asserted, whether required or not, as documentation. Perhaps in Ada 2015, we could make it mandatory, in the same way "abstract" is always re-specified. Or even now, we could make omitting "limited" obsolescent. I suppose the one downside here is in a generic, where you *want* to inherit from the formal type without having to specify whether or not the derived type is limited. So it seems like we probably want to continue to allow "limited" to be implicit in some circumstances. This might actually argue for saying you don't inherit limitness from a null record type, to keep the equivalence between an interface and a null record type. On the other hand, a null record type can easily become non-null during maintenance, whereas an interface will forever have no components. But we do need to keep an eye on this equivalence between interfaces and null records, especially with respect to generics. **************************************************************** From: Bob Duff Sent: Saturday, March 5, 2005 9:47 AM Tuck wrote: > Ok, I'm convinced. Not to preempt you, but combining various > suggestions from you, Randy, Robert Eachus, etc., I would > support the following: > > type NT is [abstract] [limited] new I1 and I2 and I3 with ... > > Limitness may be asserted on a derived type, in the > same way it may be asserted on a record type. Limitness > is *not* inherited from a parent or progenitor that is a > limited, non-synchronized interface, so limitness *must* be > asserted if the parent and progenitors are all limited, > non-synchronized interfaces, and the programmer wants > the derived type to be limited. Sounds OK. > We could go further and say that limitness is *not* > inherited from interfaces, period. That would mean > that "limited new" is *required* if the parent or any of > the progenitors are synchronized. I suppose that would > be consistent with the current rule for tagged limited > record types, where you have to say limited explicitly > even if there is a task or protected component. It > would also be consistent with our rule on defining > interfaces, where you must respecify limited (or some > other qualifier like "synchronized") always, > independent of the progenitors. That sounds OK, too. This rule might be easier for programmers to learn and remember, so I slightly prefer it. > I would allow limitedness to be asserted when it *is* > inherited, so that you can change between an interface > and a tagged type during maintenance, and not have to > remove "limited" from the places where it is now > inherited. This would be consistent with untagged > records, where we allow "limited" even when it is > redundant. I don't like allowing it. Anything that's not forbidden is required. ;-) The analogy with "limited record" doesn't work. The real rule (the one I follow in my code) is to always say "limited record" on a limited record. I think we didn't make that the language rule purely for upward compatibility. But the corresponding rule "always specify limited on a limited derived type" seems like overkill. The changing-to-interface argument is a good one. But it doesn't seem like that big of a deal -- you're changing the code anyway, and recompiling it all, so it's not so horrible to go around and add "limited" all over. > I suppose we could go further and allow "limited" to > be asserted on an untagged derived type, and in that > way support a coding convention where limited is always > re-asserted, whether required or not, as documentation. > Perhaps in Ada 2015, we could make it mandatory, > in the same way "abstract" is always re-specified. > Or even now, we could make omitting "limited" obsolescent. I could live with the option suggested as an *option*, but I'd be against making it mandatory in 2015 (or ever). Limitedness is unlike abstractness. Abstractness is a property of a specific type, and it makes no sense to inherit it. Limitedness is a class property, and should be inherited from the parent type, just like all the other operations. (Pascal is wrong to think that having a named type for the root of this hierarchy is a kludge. Trees are cleaner than forests; all hierarchies should have roots. But he's right that the mixed-syntax-and-magic-types idea was kind of ugly.) Anyway, it would not be upward compatible. Making this an option makes sense only if you think people should always do it, and compatibility prevents you from making it a real rule. But I don't think that, so I wouldn't follow such a convention. Options like this cause the language to diverge into different styles. There are at least three different styles floating around for whether to put 'in' on an in-mode parameter, and that's not helpful; Ada 83 should have made it mandatory or forbidden. > I suppose the one downside here is in a generic, where you > *want* to inherit from the formal type without having > to specify whether or not the derived type is limited. I took advantage of that in some code I wrote recently. > So it seems like we probably want to continue to allow > "limited" to be implicit in some circumstances. > This might actually argue for saying you don't inherit > limitness from a null record type, to keep the equivalence > between an interface and a null record type. That wouldn't be upward compatible. In the case I mentioned above, the thing I was inheriting from *was* a null record (sometimes limited, sometimes not). >...On the > other hand, a null record type can easily become non-null > during maintenance, whereas an interface will forever > have no components. But we do need to keep an eye on > this equivalence between interfaces and null records, > especially with respect to generics. How so? **************************************************************** From: Tucker Taft Sent: Saturday, March 5, 2005 12:30 PM >>We could go further and say that limitness is *not* >>inherited from interfaces, period. ... > > > That sounds OK, too. This rule might be easier for programmers to learn > and remember, so I slightly prefer it. Yes, it seems simpler to say that limitness is not inherited from an interface, even if it is synchronized. That applies whether the new type is an interface or is a "normal" tagged type. >> ... But we do need to keep an eye on >>this equivalence between interfaces and null records, >>especially with respect to generics. > > > How so? An interface can be passed as an actual type when the formal is an abstract limited tagged type. If you extend this formal type inside the generic, then whether you inherit limitness from it depends on whether or not the actual turns out to be an interface (based on this new proposal). That breaks the equivalence between interfaces and abstract null records, which is OK, since we know that interfaces have some special properties, but we have to be sure that this distinction doesn't break some other assumption. **************************************************************** From: Robert I. Eachus Sent: Saturday, March 5, 2005 6:19 PM My head hurts a lot after just reading this, and I am not sure I see the advantage to what could be a maintenance nightmare for both Ada programmers and compiler implementors. I can see reasons for passing an interface to a generic as an interface, and probably there is an advantage to providing a generic formal type that matches all the possibilities: type Formal is new {limited | task | protected | synchronized} interface; (Currently AI-345 seems to match this, except for the limited.) I don't quite understand, though, why a generic formal synchronized interface can't be matched by a protected interface. And I can't seem to get my mind around Tuck's comment above and AI-251. I had been thinking of interface types as a class hierarchy where an interface of lesser generality could be passed as an actual. In other words, a generic that took a synchronized interface as a formal parameter could have an actual which was a task or protected interface. Of course the interface type must be derived from within the generic to do anything useful with it, and there the hierarchy does come into play. But AI-251 seems to assume that there is no easy way to have the actual interface type do other than match the formal. I don't see the implementation difficulties--but I also don't see any reason not to have that restriction. The case that Tuck is in effect arguing for looks much more difficult than synchronized formal and protected actual interfaces, and much less useful. Am I missing something? **************************************************************** From: Pascal Leroy Sent: Monday, March 7, 2005 9:59 AM > I would > support the following: > > type NT is [abstract] [limited] new I1 and I2 and I3 with ... Fine. > We could go further and say that limitness is *not* > inherited from interfaces, period. That would mean > that "limited new" is *required* if the parent or any of > the progenitors are synchronized. That's what I prefer. It's easier to explain. And limitedness of interfaces is peculiar anyway, since a limited interface can be implemented by a limited type. (Interfaces are analogous to generic formal types from that perspective: the fact that a formal is limited doesn't guarantee that an actual is; the fact that an interface is limited doesn't guarantee that a concrete type is.) I have been playing for some time with the notion that *nonlimitedness* should be inherited for interfaces: if an interface is nonlimited, it depends on the fact that any concrete type has assignment and "=". But of course inheriting nonlimitedness is at odds with the rest of the language. Your approach is better. > I would allow limitedness to be asserted when it *is* inherited Like Bob, I don't like optional syntax. > I suppose we could go further and allow "limited" to > be asserted on an untagged derived type, and in that > way support a coding convention where limited is always > re-asserted, whether required or not, as documentation. > Perhaps in Ada 2015, we could make it mandatory, in the same > way "abstract" is always re-specified. Or even now, we could > make omitting "limited" obsolescent. Nay, don't go there, too late, and not useful enough. Jean should have done it that way back in '79. Let's blame him, as usual. > This might > actually argue for saying you don't inherit limitness from a > null record type, to keep the equivalence between an > interface and a null record type. On the other hand, a null > record type can easily become non-null during maintenance, > whereas an interface will forever have no components. But we > do need to keep an eye on this equivalence between interfaces > and null records, especially with respect to generics. If we can preserve this equivalence between interfaces and abstract null records, fine, but I wouldn't bend over backwards to do it. First, I don't think there are many abstract null records out there: when I use abstract types, I always put "common" components there; if there are no common components, sure, I have a null record, but it's a fluke rather than a conscious decision. Second, I believe that taking advantage of interfaces will require serious rearchitecturing, so people won't do that as an afternoon project by peppering a few packages with the word "interface". Chances are that massive changes will be required anyway.I am wondering now if the fact that an interface can be passed to an abstract formal type is creating a hole. Consider: type Intf is interface; generic type A is abstract tagged private; package G is type T is tagged private; private type T is new A with ... end G; package Inst is new G (Intf); It seems that the instantiation Inst is effectively violating the no-hidden-interfaces rule. Am I missing something, or should this just be illegal? **************************************************************** From: Pascal Leroy Sent: Monday, March 7, 2005 10:07 AM > Pascal is wrong to think that having a named type for the > root of this hierarchy is a kludge. Trees are cleaner than > forests; all hierarchies should have roots. You won't convince me and I won't convince me, but hopefully you'll agree that changing Ada from a forest language to a tree language is a major design decision, and should not happen as part of a last-minute fix. Not to mention that you were part of the 9X design which, in its infinite wisdom, chose to make Ada a forest language ;-) (Actually, Ada is tree-ish in the sense that all "interesting" types have to be derived from Ada.Finalization.Controlled. After 10 years of usage, it still sticks out like a sore thumb.) **************************************************************** From: Pascal Leroy Sent: Monday, March 7, 2005 10:18 AM > I don't quite understand, though, why a generic formal synchronized > interface can't be matched by a protected interface. This has been puzzling me for a long time, too, but one rather compelling answer is that there are things that you can do with synchronized interfaces and that you cannot do with protected interfaces. In particular, you can extend a protected interface to create a taskish thing. Consider: generic type SI is synchronized interface; package G is type TI is task interface and SI; task type TT is new SI with entry E; end TT; end G; If G could be instantiated with a protected interface, it would be bad news, because TI would be a task-and-protected interface, and TT would be a task-and-protected type. This could be taken care of by rechecking the rules on instantiation, and assuming-the-worst in generic bodies. But that doesn't seem like the right trade-off. **************************************************************** From: Tucker Taft Sent: Monday, March 7, 2005 12:50 PM > That's what I prefer. It's easier to explain. And limitedness of > interfaces is peculiar anyway, since a limited interface can be > implemented by a limited type. I bet you meant "by a *non*limited type." >>I would allow limitedness to be asserted when it *is* >>inherited > > > Like Bob, I don't like optional syntax. But I think you want to at least allow it in a generic, since you may not know whether the parent is an interface or a "normal" abstract tagged type. And if you want the new type to be limited even if the actual is an interface, there is no easy way to get that with this new rule. > ...I > am wondering now if the fact that an interface can be passed to an > abstract formal type is creating a hole. Consider: > > type Intf is interface; > > generic > type A is abstract tagged private; > package G is > type T is tagged private; > private > type T is new A with ... > end G; > > package Inst is new G (Intf); > > It seems that the instantiation Inst is effectively violating the > no-hidden-interfaces rule. Am I missing something, or should this just be > illegal? I think the no-hidden-interfaces rule has to be applied in the private part of an instance. Since you can't extend a formal tagged type in a body, then checking for no hidden interfaces can always be performed without breaking the contract model. I do think it is important to allow interfaces to be passed when the formal is an abstract tagged type, or else we will get into having to have two generics when one does the job. But we now have two differences between an interface and a "normal" abstract tagged type: 1) no hidden interfaces 2) limitedness not inherited These differences obviously complicate the model, but we pretty clearly can't impose the interface rules on all abstract types for compatibility reasons. Imposing the equivalence only on abstract null records doesn't really help, because that just moves the "bump" to a new place, since you can't specify "nullness" of a generic formal abstract tagged type. So to summarize my suggestion: 1) You never inherit limitness from an interface; 2) You can always assert limitness on a type extension, to address the possibility that the parent is a formal abstract tagged type whose actual is a limited interface. Part (1) above jibes with the preference expressed by you and Bob. Part (2) above, which violates the "optional syntax" is bad metarule, I claim is analogous to the optional "limited" for record types with limited components -- think of a non-interface parent like a component, and you get pretty much the same model. Predefined equality also treats a non-interface parent like a component, so there is some precedent there... **************************************************************** From: Randy Brukardt Sent: Monday, March 7, 2005 1:45 PM > It seems that the instantiation Inst is effectively violating the > no-hidden-interfaces rule. Am I missing something, or should this just be > illegal? It *is* illegal, because you always recheck legality rules in the spec. of an instance. Certainly this one doesn't pass that recheck. In the body, we don't allow deriving from a formal type anyway, for the precise reason that we can't recheck the legality rules, and there are a number of (existing) ones that can fail. This is just one more to add to that list. (The legality rule certainly apply in the private part of an instance; I've never understood why that isn't the default, *not* checking legality rules should be the unusual special case.) **************************************************************** From: Tucker Taft Sent: Monday, March 7, 2005 2:30 PM I think we were trying to preserve the "fiction" that privacy applies when instantiating a generic. But alas, in many cases, you need to look in the private part of the generic to know whether a given instantiation will be legal. I guess the real "privacy" rule that applies to generics is that to *use* an instance of a generic, you don't need to look in the private part. But to instantiate a generic, you probably do need to look to see how the formal types are actually used in the private part. I guess that shouldn't be *too* surprising... **************************************************************** From: Bob Duff Sent: Monday, March 7, 2005 3:12 PM Pascal wrote: > You won't convince me and I won't convince me, ... And *I* won't convince *me*. ;-) >... but hopefully you'll agree > that changing Ada from a forest language to a tree language is a major > design decision, and should not happen as part of a last-minute fix. Yes, I agree. > Not to mention that you were part of the 9X design which, in its infinite > wisdom, chose to make Ada a forest language ;-) Hmm. I don't remember whether we proposed all of the Root_Limited and friends, but I do remember that we proposed a Root_Task type, and got tarred and feathered for it. To this day, I think that was a cleaner solution than the Task_Ids package, which is untypesafe and allows dangling pointers. But I'm not pushing to add Root_Task *now*. > (Actually, Ada is tree-ish in the sense that all "interesting" types have > to be derived from Ada.Finalization.Controlled. After 10 years of usage, > it still sticks out like a sore thumb.) Well, I still use untagged records quite a bit. There are many places in my code where the overhead of finalization (given most compiler's implementations) would be intolerable. I also use 'limited' quite a lot, because ":=" doesn't work, and I don't want Controlled. You may have noticed me pushing for various AI's that make limited types usable. ;-) **************************************************************** From: Bob Duff Sent: Monday, March 7, 2005 5:21 PM > But I think you want to at least allow it in a generic, OK, I'm convinced. I don't entirely buy your limited-records analogy, but the above generics argument is pretty compelling. **************************************************************** From: Pascal Leroy Sent: Tuesday, March 8, 2005 5:17 AM > > Like Bob, I don't like optional syntax. > > But I think you want to at least allow it in a generic, since > you may not know whether the parent is an interface or a > "normal" abstract tagged type. And if you want the new type > to be limited even if the actual is an interface, there is no > easy way to get that with this new rule. You're right. But if you allow it in a generic, you have to allow it everywhere, because you want to make it easy to turn a nongeneric package into a generic (or vice-versa). > I do think it is important to allow interfaces to be passed > when the formal is an abstract tagged type, or else we will > get into having to have two generics when one does the job. I am not sure if I agree with this. If it comes for free, fine, but if it hairs up the language, it bothers me. I guess I don't see the need as compelling enough. On the other hand if you cannot pass an interface to a generic formal abstract, it means that you don't have a "most general formal tagged type" and that's unpleasant. > But we now have two differences between an interface and a > "normal" abstract tagged type: > > 1) no hidden interfaces > 2) limitedness not inherited > > These differences obviously complicate the model, but we > pretty clearly can't impose the interface rules on all > abstract types for compatibility reasons. Imposing the > equivalence only on abstract null records doesn't really > help, because that just moves the "bump" to a new place, > since you can't specify "nullness" of a generic formal > abstract tagged type. > > So to summarize my suggestion: > 1) You never inherit limitness from an interface; > 2) You can always assert limitness on a type extension, > to address the possibility that the parent is a formal > abstract tagged type whose actual is a limited interface. I strongly suggest writing an AI. **************************************************************** From: Tucker Taft Sent: Tuesday, March 8, 2005 8:02 AM I thought you were going to write one. Did I preempt you with my blabbing so that now it is mine? Let me know... **************************************************************** From: Pascal Leroy Sent: Tuesday, March 8, 2005 11:02 AM I guess you touched it last, so yes, it's yours. ****************************************************************