!standard 03.10.01 (02) 03-03-18 AI95-00217-05/02 !standard 03.10.01 (03) !standard 03.10.01 (04) !standard 03.10.01 (05) !standard 03.10.01 (08) !standard 03.10.01 (09) !standard 03.10.01 (10) !standard 03.10.01 (11) !standard 10.01.02 (03) !standard 10.01.02 (04) !standard 10.01.02 (08) !standard 10.02 (04) !standard J.11 (00) !class amendment 03-01-16 !status work item 03-01-16 !status received 03-01-16 !priority Medium !difficulty Hard !subject Type stubs with limited context clauses !summary A pair of new constructs, called "type stub"s and "limited with clause"s are added to the Standard to provide a solution to the "mutually recursive types across packages" problem. A limited with clause makes a compilation unit available for limited use. Some details of such a unit can be used in the current compilation unit without creating a semantic dependence. A type stub is a kind of incomplete type declaration which is completed in a package specified by a limited with. Compile-time checks are performed on use of the name introduced by the type stub that the completion is "available" and "appropriate" (see !proposal for details). !problem Ada allows mutually recursive types to be declared only if they are all declared within the same library unit. This can force all of the major data structures of a program into a single library unit, which is clearly undesirable in some situations. The goal of the proposed features is to allow mutual recursion among separately compiled types (types containing (pointers to) each other as components, and primitive operations with (pointers to) each other as parameters), and to do this in a way that doesn't place any undue restrictions on the programmer. !proposal The syntax for a context_item is amended as follows: context_item ::= with_clause | use_clause | limited_with_clause limited_with_clause ::= [PRIVATE] LIMITED WITH limited_unit_specifier; limited_unit_specifier ::= identifier | limited_unit_specifier . identifier; The limited_with_clause does not create a semantic dependence on the specified unit. The specified unit can only be used only in contexts that specifically allow the use of limited_unit_specifiers; this clause do not declare a name that can be generally used. The limited_unit_specifier specifies the full expanded name of a compilation_unit. (It's harmless to allow non-packages here, and we can't check anyway.) The limited_unit_specifier can be used within the declarative region of the compilation_unit, unless the keyword private appears, in which case it may only be used in private parts and bodies. (This is the same rule as for "private with", see AI-262.) The unit specified by the limited_with_clause is needed if the compilation unit on which it appears is needed - see 10.2. (This insures that the completion appears in the partition.) A post-compilation check is made that any unit which has a limited_with_clause is included in the semantic closure of the unit named by the limited_unit_specifier. This includes a check that limited_unit_specifier specifies the full expanded name of a library compilation_unit (starting with a root library unit, and using no renaming declarations). (This allows most checks to be accomplished at the point of completion, and also simplifies the implementation for some compilers.) The syntax for an incomplete type declaration is amended as follows: incomplete_type_declaration ::= TYPE defining_identifier [discriminant_part]; | type_stub; [Note: tagged incomplete types are now defined in AI-326.] A new kind of incomplete type declaration is added: type_stub ::= TYPE defining_identifier [discriminant_part] IS [ TAGGED ] SEPARATE OF limited_unit_specifier . type_identifier; A type_stub introduces an incomplete type whose completion occurs in the unit identified by the limited_unit_specifier, and whose defining_identifier is type_identifier. The limited_unit_specifier must appear in a limited_with_clause for the compilation_unit. If the word TAGGED appears, the type is a tagged incomplete type (see AI-326). The limited_unit_specifier is not subjected to the name resolution rules at the place of the type_stub. In order to be a completion, the completing type declaration must be in the visible part of the unit specified by the limited_unit_specifier, must not be an incomplete type declaration, and must satisfy all other rules for incomplete type completion. A check is also made that there is no more than one type stub for the completing type declaration. (These checks can be made in the compilation unit containing the completing type declaration, because any type stubs must be in the semantic closure of the unit by the rule above.) Each type stub that specifies a particular unit must be completed in that unit. When a name introduced by a type stub is used in a context where a complete type is required (which is described above), a check is made that the usage place is within the immediate scope of the completion, or is within the scope of a with clause that mentions the unit identified by the type stub's limited_unit_specifier. Deferences of an access-to-incomplete type are allowed in limited additional contexts. !wording Replace 3.10.1(2): incomplete_type_declaration ::= TYPE defining_identifier [discriminant_part]; | type_stub type_stub ::= TYPE defining_identifier [discriminant_part] IS [ TAGGED ] SEPARATE OF limited_unit_specifier . type_identifier; limited_unit_specifier ::= identifier | limited_unit_specifier . identifier; Modify 3.10.1(3): An incomplete_type_declaration other than a type_stub requires a completion, which ... Insert 3.10.1(3.1): A type_stub includes a limited_unit_specifier which specifies the full expanded name of the compilation unit in which its completing type is expected to occur. [Certain uses (see below) of a name that denotes the type_stub or a value of an access type that designates the type_stub, require that the completing type exist.] For a type_stub, the completing type shall occur in the visible part of the specified unit, and be a type_declaration other than an incomplete_type_declaration; the type_identifier shall be the same as the defining_identifier of the completing type. Each completing type shall have no more than one type_stub. Modify 3.10.1(4): Replace all occurrences of "full_type_declaration" with "type_declaration" in the existing sentences, so that a private type (or private extension) can complete a type stub. Replace 3.10.1(5): An incomplete_type_declaration defines an "incomplete view" of a type. An incomplete view and the corresponding completing type_declaration define two views of the same type. The only allowed uses of a name that denotes an incomplete view are as follows: Replace 3.10.1(10): If a name that denotes an incomplete view is used in other contexts, the complete view shall be *available* at the place of use, as defined below: * If the incomplete view is declared by a type_stub, the complete view is available if * the place of use of the name is within the immediate scope of the completing type of the type_stub; or * the place of use is within the scope of a with_clause that mentions the unit [(including a library-level renaming)] specified by the limited_unit_specifier of the type_stub. * If the incomplete view is not declared by an incomplete_type_declaration that is not a type_stub, its complete view is available throughout the (extended) scope of the completion. * If the incomplete view is a class-wide type, its complete view is available if the complete view of the root of the class is available. If the complete view of an incomplete view is available, the complete view is used instead of the incomplete view. A dereference (implicit or explicit) of a value of an access type whose designated type D is an incomplete view is allowed only in the following contexts: * in a place where the complete view of D is available (see above); * in a context where the expected type is E and o E covers the complete view of D, o E is tagged and covers D, o E covers D'Class or its complete view, or o E'Class covers D or its complete view; * as the target of an assignment_statement where the type of the value being assigned is V, and V or V'Class is the complete view of D. Replace 10.01.2(3): context_item ::= with_clause | use_clause | limited_with_clause Add after 10.01.2(4): limited_with_clause ::= [PRIVATE] LIMITED WITH limited_unit_specifier; Add after 10.01.2(8): The limited_unit_specifier of an limited_with_clause that appears on a library_unit_declaration or library_unit_renaming_declaration may be used in entire declarative region of the declaration, which includes all children and subunits. The limited_unit_specifier of an limited_with_clause that appears on a body may be used in that body, including all subunits. A limited_unit_specifier defined only in abstract_clauses that include the reserved word private shall be used only within * a private part, * a body, * a private descendant of the unit on which one of these limited_with_clauses appear. Post-compilation rules The limited_unit_specifier of a limited_with_clause shall specify the full expanded name of a library compilation_unit (starting with a root library unit, and using no renaming declarations). The unit that includes the limited_with_clause in its context_clause shall be included in the semantic closure of the unit specified by the limited_unit_specifier. Add after 10.02(4): * If a compilation unit is needed, then so are any compilation units specified by the limited_unit_specifiers in limited_with_clauses of its context_clause; Add J.11: J.11 The Class Attribute of Non-tagged Incomplete Types For the first subtype S of a type T declared by an incomplete_type_declaration that is not tagged and is not a type stub, the following attribute is defined: S'Class Denotes the first subtype of the incomplete class-wide type rooted at T. The completion of T shall declare a tagged type. Such an attribute reference shall occur in the same library unit as the incomplete_type_declaration. !discussion We considered calling these "separate incomplete types", but we felt it was more consistent to use the term "stub" rather than "separate" to be consistent with the program unit "stub" terminology. An limited_with_clause does not declare a name. This was done so that we do not have to introduce a "ghost" package in order to explain the presence of this name. We also do not want to be able to use the limited_unit_specifier other than in a stub of some sort (with a type stub being the only kind allowed currently). If it is a name, it could be used in other contexts, requiring the insertion of checks that this is not the case into an implementation. We considered a variety of other syntaxes for limited_with_clauses. Several syntaxes that don't look like with_clauses. Since a limited_with_clause neither declares a name nor creates a semantic dependence, it doesn't seem that much like a with_clause. Thus, some reviewers preferred a different syntax. Two of the syntaxes considered were: [private] abstract of P; which was rejected because describing the model it implied was very awkward, and [private] use type in P; which was rejected because it is only one keyword different from "use type", and may cause confusion. We decided to stick with the familar with_clause syntax, as there are also similarities with regular with clauses: -- The unit named is needed in the partition; -- Dependencies between units are created. An alternative syntax using "separate" rather than "limited" was also considered, but that did not seem to have the correct connotation. There is no particular restriction on where a type stub may occur, unlike the restriction on program unit stubs. In particular, a type stub may occur in a declare block, a subprogram body, or anywhere an incomplete type declaration may occur. Of course, they are generally only useful if used in a place where a separately compiled unit can see the stub as well as the completing package, so putting them in a declare block, or a subprogram body without any program unit stubs, would not be of any great use. There seem to be two general ways that the problem of mutually recursive types is solved in other languages. One approach is to permit unrestricted forward references. This is the approach adopted by Eiffel, Java, and Smalltalk. The other approach is to require "forward" or "incomplete" declarations, sometimes called "signatures" or "partial revelations." This is generally the approach adopted by Ada, as well as Pascal, Modula, ML, C/C++, etc., and is the approach adopted here for cross-package mutual recursion. We chose the simple approach of extending a single existing Ada feature, rather than adding a basket of heavy new features to provide the needed capability. This simplifies both the conceptual burden and the implementation cost. The model of a type stub per se does not add any significant new implementation burden, because it is very similar to the incomplete-deferred-to-body type which Ada 95 already has. The compiler does not need to know the real type involved as long as the usage rules for incomplete types are enforced. Determining the representation of an access type that designates a type stub could be a problem for some implementations that select the representation of an access type based on the designated type. However, this would be no different than for a type whose completion is deferred to the body. Such implementations must already be able to handle this somehow. Since an access type that designates a type stub is a normal declaration, representation clauses (for storage pools, for instance) can be used as needed. This eliminates many of the problems found in the "with type" proposal. Note that such an access type can be used normally (including allocators, deallocators, and the like) when the completing type is visible. The key difference here from the "with type" proposal is that type stubs, and any associated access type declarations, are "real" declarations that can be referenced from other packages. A "with type" clause would have had the same rules of a "with" clause, which means that they would not be visible to other packages. Instead, each package would require its own with type clause, making it harder to share access types, and requiring trickier visibility rules to deal with "incomplete" packages as well as incomplete types. We introduce the idea of an "incomplete view" in this proposal. By so doing, we eliminate any need to talk about type matching, as there is only one type. Also note that we talk about a "completing type" for a type stub, rather than a completion. This avoids triggering the "hidden from all visibility" rules of 8.3 on type stubs. We don't need these rules for type stubs, as it is not possible for the same name to denote both an incomplete view of a type stub and a complete view (the names are necessarily distinct). This property is not shared by any of the alternative proposals. Note that the "hidden from all visibility" rules cause significant problems with ripple effects (that is, indirect semantic dependence: cases where adding or deleting a context clause item from a distant unit changes the legality of some other compilation unit). Once we have the notion of an incomplete view, we could go further and make incomplete types first-class types, and thus allow the declaration of subtypes of them. This would be easy to do, but it doesn't seem to have any particular value. As unnecessary changes increase the risk of introducing errors, we did not make this change. Conceptual and implementation difficulties arise from the question where the type is to be regarded as completed and hence allows for object creation and availability of operations of the type. It is clear that such a place must have a semantic dependence on (the package containing) the completing type. However, to avoid ripple effects (see above), we say that the complete view is only "available" when within the immediate scope of the completing type itself, or the scope of a with clause that mentions the completing unit. This rule is relaxed when the completing type is in the immediate context of the usage (i.e., the incomplete view is used in a context where it matched the complete view). We considered other rules, such as requiring only semantic dependence (rejected due to the ripple effects), or creating an implicit semantic dependence at the point of a usage where the completion was needed (rejected due to difficulties for some implementations, and loss of visible "documentation" of the dependence). When the complete view of an incomplete view is available, the complete view is used in place of the incomplete view. This allows all of the operations of the complete view to work as expected. These rules have no effect on "old" incomplete types, as such incomplete views are always "hidden from all visibility". We considered modifying the "hidden from all visibility" rules to handle these cases as well, but the changes needed were substantial, and thus were likely to introduce errors into other parts of the language. This proposal includes post-compilation rules to insure that the completing type always has any type_stubs in its semantic closure. This makes type stubs slightly harder to use, but insures that we can perform all needed checking on the legality of the completing type when the completing unit is compiled. An additional rule insures that the completing type's unit must be included in the partition. Combined with the requirement that the completing type appear in the appropriate unit, we insure that all stubs are completed. In addition, we require that there is at most one stub for each completing type. This rule was originally adopted in order to eliminate the need for special type matching rules. However, with the adoption of the "incomplete view" model, we no longer need such rules anyway. We've retained the single stub requirement because it appears to make implementation easier for some implementation. [Note: An earlier draft of this AI said that allowing multiple stubs was necessary in order for type stubs to be usable with generics. If true, the rule should be removed.] The rule requiring that the completing type always has any type_stubs in its semantic closure could be accused of having a ripple effect. After all, removing a with_clause from some unit could make the partition illegal. We could have used wording requiring the with_clause directly on the unit referenced by the limited_with. However, note that it is the partition as a whole that is illegal, not any particular unit. The unit containing the completing type is perfectly legal as long as the unit with the type_stub and limited_with_clause isn't needed by the partition. Moreover, the binder error message for this error is likely to say "unit P needs a with_clause for unit Q as it has a limited_with_clause for P"; the user will simply add the needed clause and move on. As semantic closure is likely to be easier to check, we require the weaker condition. This proposal does not allowing type stubs to refer to local or nested completing types. This capability could be added by extending the syntax of type stubs to: type_stub ::= TYPE defining_identifier [discriminant_part] IS [ TAGGED ] SEPARATE OF [limited_unit_specifier .] {package_identifier .} type_identifier; and adding appropriate additional wording. If this is done, "old" incomplete types are usually just a shorthand for a type stub specifying the same unit. (If we allowed completions in private parts and bodies, this would be complete.) This was not done for multiple reasons. First, this syntax appears ambiguious (it is not possible to syntactically distiguish limited_unit_specifier from other identifiers). Second, the wording to describe the checks and meaning of this is complicated. Third, given that Ada includes child units, the nested unit containing a completing type can almost always be placed into a child unit, whereupon type_stubs as defined here can be used. Similarly, existing Ada facilities often can be used in that case, eliminating the need for stubs. Lastly, packages large enough to need this facility are quite rare. We considered having some restriction relating to specifying private child packages. However, it was unclear what the rule should be, and there seemed no particular benefit in adding the complexity. Clearly if the completion is in a private child, only packages that have visibility on that child can make use of the completion, and can dereference an access-to-type-stub. But there seems no particular harm in allowing the type stub to specify a private child. Without actually "looking" at the specified package, it may not be easy to determine whether it is a private child. If the check is made when the stub is completed, then a somewhat artificial check needs to be made that the location of the type stub is inside the subsystem where the completing package is visible. An important property is that changing a private child, or adding or removing a private child, have no effect outside its "subsystem." That remains true even without any additional check, since the type stub by itself is legal, it is the usages that would be affected, and they would all have to be inside the subsystem. !example Here is the classic case of mutual dependence, where an employee belongs to a particular department, and a department has a manager who is an employee. (We assume the use of tagged types here to illustrate the use of tagged incomplete types.) The example uses separate packages as package abstracts, which is usually advisable to have single well defined place for the access types. If it is desired to have the access type also be available in the completing package, that can be accomplished with a subtype declaration, as illustrated in package Employees. limited with Employees; package Employees_Abstract is type Employee is tagged separate of Employees.Employee; type Emp_Ptr is access all Employee'Class; end Employees_Abstract; limited with Departments; package Departments_Abstract is type Department is tagged separate of Departments.Department; type Dept_Ptr is access all Department'Class; end Departments_Abstract; with Departments_Abstract, Employees_Abstract; package Employees is type Employee is tagged private; subtype Emp_Ptr is Employees_Abstract.Emp_Ptr; procedure Assign_Employee(E : in out Employee; D : in out Departments_Abstract.Department'Class); ... function Current_Department(D : in Employee) return Departments_Abstract.Dept_Ptr; end Employees; with Departments_Abstract, Employees_Abstract; package Departments is type Department is tagged private; procedure Choose_Manager(D : in out Department; Manager : in out Employees_Abstract.Employee'Class); ... end Departments; --Note: Corrigendum sections not updated. --!corrigendum 3.10.1(2) -- --@drepl --@xindent<@fa defining_identifier [discriminant_part];>> --@dby --@xindent<@fa defining_identifier [discriminant_part] [@b];@hr -- | type_stub>> -- --@xindent<@fa defining_identifier [discriminant_part] @b [@b]@hr -- @b abstracted_package_specifier;>> -- --@xindent<@fa> -- --!corrigendum 3.10.1(3) -- --@drepl --An @fa requires a completion, which shall be a --@fa. If the @fa occurs --immediately within either the visible part of a @fa --or a @fa, then the @fa shall occur --later and immediately within this visible part or @fa. If --the @fa occurs immediately within the private part --of a given @fa, then the @fa --shall occur later and immediately within either the private part itself, or --the @fa of the corresponding @fa. --@dby --An @fa other than a @fa requires a --completion, which shall be a @fa. If the --@fa occurs immediately within either the visible --part of a @fa or a @fa, then the --@fa shall occur later and immediately within this --visible part or @fa. If the @fa --occurs immediately within the private part of a given --@fa, then the @fa shall occur --later and immediately within either the private part itself, or the --@fa of the corresponding @fa. -- --A @fa includes a @fa which specifies --the full expanded name of the package in which its completion is expected to --occur. Certain uses (see below) of a name that denotes the @fa or --a value of an access type that designates the @fa, require that --the completion exist. In these cases, the completion shall occur in the --visible part of the specified package, and be a @fa other --than an @fa; the @fa --shall be the full expanded name of this package (starting with a root --library unit, and using no renaming declarations), and the package shall be --a library package. -- --!corrigendum 3.10.1(4) -- --@drepl --If an @fa has a @fa, then --a @fa that completes it shall have a fully conforming --(explicit) @fa (see 6.3.1). If an --@fa has no @fa (or an --@fa), then a corresponding @fa --is nevertheless allowed to have discriminants, either explicitly, or inherited --via derivation. --@dby --If an @fa includes the keyword @b, then a --@fa that completes it shall declare a tagged type. --If an @fa has a @fa, then --a @fa that completes it shall have a fully conforming --(explicit) @fa (see 6.3.1). If an --@fa has no @fa (or an --@fa), then a corresponding @fa --is nevertheless allowed to have discriminants, either explicitly, or inherited --via derivation. In the case of a @fa, these checks are performed no --later than when a construct requires the completion to be available. -- -- --!corrigendum 3.10.1(5) -- --@drepl --The only allowed uses of a name that denotes an @fa --are as follows: --@dby --A name that denotes an @fa may be used as follows: -- --!corrigendum 3.10.1(8) -- --@drepl --@xbullet in an @fa;> --@dby --@xbullet in an @fa.> -- --A name that denotes an incomplete_type_declaration that includes the --keyword @b may also be used as follows: -- --@xbullet defining the subtype of a parameter in a --@fa;> -- -- --!corrigendum 3.10.1(9) -- --@drepl --@xbullet of an @fa whose --@fa is Class; such an @fa is --similarly restricted to the uses allowed here; when used in this way, the --corresponding @fa shall declare a tagged type, and the --@fa shall occur in the same library unit as the --@fa.> --@dby --@xbullet of an @fa whose --@fa is Class; such an @fa is --restricted to the uses allowed above.> -- --If a name that denotes an @fa is used in other --contexts, the @fa shall be a @fa, and --the completion shall be @i at the place of use, as defined by --either of the following conditions: -- --@xbullet; or> -- --@xbullet that --mentions the package specified by the @fa of the --@fa.> -- --The completion of an @fa that is not a --@fa is defined to be available throughout the (extended) scope --of the completion. The completion of an incomplete class-wide type is --available wherever the completion of the root of the class is available. -- --!corrigendum 3.10.1(10) -- --@drepl --A dereference (whether implicit or explicit -- see 4.1) shall not be of an --incomplete type. --@dby --A dereference (implicit or explicit -- see 4.1) of a value of an access type --whose designated type @i is incomplete is allowed only in the following --contexts: -- --@xbullet is available (see above);> -- --@xbullet and> --@xinbull<@i covers the completion of @i,> --@xinbull<@i is tagged and covers @i,> --@xinbull<@i covers @i'Class or its completion, or> --@xinbull<@i'Class covers @i or its completion;> -- --@xbullet where the type of the --value being assigned is @i, and @i or @i'Class is the completion of --@i.> -- --In these contexts, the incomplete type is defined to be the same type as --completion, and its first subtype statically matches the first --subtype of its completion. -- --!corrigendum 3.10.1(11) -- --@drepl --An @fa declares an incomplete type and its first --subtype; the first subtype is unconstrained if a @fa --appears. --@dby --An @fa declares an incomplete type and its --first subtype; the incomplete type is tagged if the keyword @b --appears; the first subtype is unconstrained if a @fa --appears. Two @fas are defined to be the same type if they have the --same defining identifier, the same sequence of identifiers in their --@fas, and their first subtypes match statically. -- --!corrigendum 10.01.2(3) -- --@drepl --@xindent<@fa> --@dby --@xindent<@fa> -- --!corrigendum 10.01.2(4) -- --@dinsa --@xindent<@fa @iname {, @iname }>> --@dinst --@xindent<@fa] @b abstracted_package_specifier;>> -- --!corrigendum 10.01.2(8) -- --@dinsa --If a @fa of a given @fa mentions a private child --of some library unit, then the given @fa shall be either the --declaration of a private descendant of that library unit or the body or subunit --of a (public or private) descendant of that library unit. --@dinss --The @fa of an @fa that appears on --a @fa or @fa may be --used in entire declarative region of the declaration, which includes --all children and subunits. The @fa of an --@fa that appears on a body may be used in that body, --including all subunits. -- --A @fa defined only in @fas that --include the reserved word @b shall be used only within --@xbullet --@xbullet --@xbullets appear.> -- --!corrigendum J.10(1) -- --@dinsc --For the first subtype S of a type T declared by an --@fa that is not tagged and is not a type stub, the --following attribute is defined: -- --@xhang<@xtermDenotes the first subtype of the incomplete class-wide --type rooted at T. The completion of T shall declare a tagged type. Such an --attribute reference shall occur in the same library unit as the --@fa. !ACATS test A series of B and C tests should be constructed for this proposal. !appendix [For discussion on the once-approved version of this proposal, see AI-00217-04.] ************************************************************* From: Pascal Leroy Sent: Tuesday, December 17, 2002, 5:03 AM I am forwarding the following message from John to the ARG mailing list because I would like to get the ball rolling. AI 217 was rejected by WG9 because of the objection/proposal below. I want to get a resolution on this issue at the Padua meeting, so that we can send this AI (which I view as very important) to WG9 in June. As John points out, moving the stub declaration at the front of the unit brings us back to the "with type" proposal, and we know that this one is full of problems. The notion of having a new kind of with clause for the sole purpose of human readability makes some sense, but then there is very little semantics there, and I am not too excited by the idea of adding a completely new construct which has about the same value as a comment (OK, not quite). What do other people think? Pascal -- > AI-217 is type stubs of course. > > The issued raised at the presentation made by Tuck was that > many people from the floor objected to a package P being > mentioned in the stub > > type T is ... separate in package P. > > inside the text of a unit Q and that there was no mention of > P in the context clause for Q. > > I think the audience took on board the fact that the whole > point was that Q must not depend on P and so a regular with > clause was not possible. However they did not like the > textual lurking and wanted some sort of up-front mention if > only for the benefit of the human reader. > > What I thought was a sound suggestion from the floor was > that maybe we could have a "with out" clause for P which > gave the human reader visibility but no Ada dependence. This > was greeted with a certain amount of mirth (perhaps because > of the way in which I suggested it). > > One other suggestion from the floor was that maybe the whole > stub declaration should be up front but that seemed > unhealthy to me and would have a flavour of going back to > the with type idea. > > Anyway, that was it. ************************************************************* From: Robert A Duff Sent: Tuesday, December 17, 2002, 8:31 AM > I am forwarding the following message from John to the ARG mailing > list because I would like to get the ball rolling. > > AI 217 was rejected by WG9 because of the objection/proposal below. This seems like, how does that saying go, "perfection is the enemy of the good", or something like that? Technically, the objection is right: there *should* be some mention of that package in the context clause. But from a practical point of view: As an Ada user, I've been waiting desperately for a solution to this serious language flaw for 7 years. We've finally got a solution that works, and now folks want to hold it up for perhaps another year or more to solve a relatively minor problem? Grrr. >...I > want to get a resolution on this issue at the Padua meeting, so that > we can send this AI (which I view as very important) to WG9 in June. Wishful thinking. The ARG has never been able to work that quickly. At the Padua meeting, we will be in the "Norm Cohen mode", of cobbling together vaguely readable syntax out of existing reserved words (because adding new reserved words is viewed in the Ada community as morally akin to consorting with the devil). Then somebody will go off and write it up, and we will need *another* meeting to actually vote on it. And there's a nonnegligible probability that we will get bogged down in an argument about the tastefulness of the proposed syntax (see "mirth" below), requiring a third meeting. Historically, no serious technical work gets done *between* meetings. OK, enough ranting. I can live with the proposal as is, or I can live with an extra thing in the context clause, or I can live with putting the whole thing in the context clause if that works. > As John points out, moving the stub declaration at the front of the > unit brings us back to the "with type" proposal, and we know that this > one is full of problems. Could someone explain that in more detail? Why should merely shuffling the syntax around cause trouble? Can we shuffle the syntax without changing the semantics? > The notion of having a new kind of with clause for the sole purpose of > human readability makes some sense, but then there is very little > semantics there, and I am not too excited by the idea of adding a > completely new construct which has about the same value as a comment > (OK, not quite). Ada has lots of features whose primary purpose is "comment", except with one huge advantage: the reader has some confidence that you're not lying, because the compiler checks the rules. So I don't mind having a new kind of with clause that is merely a (compiler-checked) comment. > What do other people think? > > Pascal > -- > > AI-217 is type stubs of course. > > > > The issued raised at the presentation made by Tuck was that > > many people from the floor objected to a package P being > > mentioned in the stub > > > > type T is ... separate in package P. > > > > inside the text of a unit Q and that there was no mention of > > P in the context clause for Q. > > > > I think the audience took on board the fact that the whole > > point was that Q must not depend on P and so a regular with > > clause was not possible. However they did not like the > > textual lurking and wanted some sort of up-front mention if > > only for the benefit of the human reader. > > > > What I thought was a sound suggestion from the floor was > > that maybe we could have a "with out" clause for P which > > gave the human reader visibility but no Ada dependence. This > > was greeted with a certain amount of mirth (perhaps because > > of the way in which I suggested it). I can live with "with out". But I can see why some folks find it silly. How about: with separate P; or with separate type in P; -- Norman would like that. ;-) The Legality Rule would presumably be that if you say "type T is separate in package P;", you must be within the scope of a "with separate P;" clause. > > One other suggestion from the floor was that maybe the whole > > stub declaration should be up front but that seemed > > unhealthy to me and would have a flavour of going back to > > the with type idea. > > > > Anyway, that was it. Tucker mentioned another issue: Some people use the style where every type in the program has the same name: "Object" or "T" or "Instance", and the actual semantics is encoded in the package name. So if you want types "Doctors_Package.Object" and "Patients_Package.Object" to be mutually recursive, you can't do it. Is that the issue, Tuck? My response is, "So don't do that." Besides, if you really want it to be called "Object", can't you always rename "subtype Object is Doctor_Package.Doctor_Type;" to work around the problem? Is there a better solution? ************************************************************* From: Pascal Leroy Sent: Tuesday, December 17, 2002, 9:16 AM > This seems like, how does that saying go, "perfection is the enemy of > the good", or something like that? Technically, the objection is right: > there *should* be some mention of that package in the context clause. > But from a practical point of view: As an Ada user, I've been waiting > desperately for a solution to this serious language flaw for 7 years. > We've finally got a solution that works, and now folks want to hold it > up for perhaps another year or more to solve a relatively minor problem? > Grrr. I couldn't agree more. > Wishful thinking. You're probably right :-( > > As John points out, moving the stub declaration at the front of the > > unit brings us back to the "with type" proposal, and we know that this > > one is full of problems. > > Could someone explain that in more detail? Why should merely shuffling > the syntax around cause trouble? Can we shuffle the syntax without > changing the semantics? I suppose you could, but that doesn't make sense to me. You would have a declaration occurring in a context clause. That's a whole new mechanism, and certainly not an intuitive one. I believe we want to preserve the invariant that the context clause describes what is imported by a unit, and the visible part contains the declaration exported by the unit. > Ada has lots of features whose primary purpose is "comment", except with > one huge advantage: the reader has some confidence that you're not > lying, because the compiler checks the rules. So I don't mind having a > new kind of with clause that is merely a (compiler-checked) comment. That's true in general, but here I fail to see that it buys you any safety at all. The only reason why you would have to type this new with clause is so that the compiler can check the type stubs against it. But then the compiler and/or linker are surely going to check the package identifier occurring in the type stub at some point. So what's the big deal? Consider the following analogy: body stubs can be scattered in a unit and they do not need to be declared at the beginning of that unit. Would it be useful to require such declarations? I don't think so. OK, occasionally I forget to provide a subunit, and I only discover the problem at link time. But it doesn't happen often in real life, and it doesn't compromise the safety of my programs. > How about: > > with separate P; > > or > > with separate type in P; -- Norman would like that. ;-) That sounds better than "with out". > Tucker mentioned another issue: Some people use the style where every > type in the program has the same name: "Object" or "T" or "Instance", > and the actual semantics is encoded in the package name. So if you want > types "Doctors_Package.Object" and "Patients_Package.Object" to be > mutually recursive, you can't do it. Is that the issue, Tuck? > > My response is, "So don't do that." > > Besides, if you really want it to be called "Object", can't you always > rename "subtype Object is Doctor_Package.Doctor_Type;" to work around > the problem? Is there a better solution? This particular topic was discussed at the Cupertino meeting, and the conclusion was that this was not important enough to make the construct more complex. I don't think we should revisit decisions that have been made after extensive discussion, unless there is a new technical element that could modify the conclusion. Otherwise we are just never going to make any progress. ************************************************************* From: Barbey, St‚phane Sent: Tuesday, December 17, 2002 9:22 AM Robert A. Duff wrote: > Tucker mentioned another issue: Some people use the style where every > type in the program has the same name: "Object" or "T" or "Instance", > and the actual semantics is encoded in the package name. So if you want > types "Doctors_Package.Object" and "Patients_Package.Object" to be > mutually recursive, you can't do it. Is that the issue, Tuck? > > My response is, "So don't do that." ... except it is heavily used by the IDL2Ada compilers (and BTW IDL has a concept of forward declarations that would be much easier to work with in Ada with if the ARG gets this AI right...) ************************************************************* From: Pascal Leroy Sent: Tuesday, December 17, 2002, 10:06 AM The AI explains (in the !example section) that it is advisable to introduce an _Interface package to declare the type stubs. The reason is that if you have types that are involved in more than one cycle, this provides the best structure. With this approach, the example from the AI works fine even if everything is named Object: package Employees_Interface is type Object is tagged separate in Employees; type Emp_Ptr is access all Object'Class; end Employees_Interface; package Departments_Interface is type Object is tagged separate in Departments; type Dept_Ptr is access all Object'Class; end Departments_Interface; with Departments_Interface, Employees_Interface; package Employees is type Object is tagged private; procedure Assign_Employee(E : in out Object; D : in out Departments_Interface.Object); ... function Current_Department(D : in Object) return Departments_Interface.Dept_Ptr; end Employees; with Departments_Interface, Employees_Interface; package Departments is type Object is tagged private; procedure Choose_Manager(D : in out Object; Manager : in out Employees_Interface.Object); ... end Departments; With this approach the _Interface package effectively works as a forward declaration. The issue raised by Bob only exists if there is no _Interface package and Employees and Departments directly declare type stubs for each other. I don't know what IDL compilers do, but it would be much easier to systematically generate an _Interface package, than to detect the case where direct dependence is possible. ************************************************************* From: Ben Brosgol Sent: Tuesday, December 17, 2002 10:41 AM > > This seems like, how does that saying go, "perfection is the enemy of > > the good", or something like that? Technically, the objection is right: > > there *should* be some mention of that package in the context clause. > > But from a practical point of view: As an Ada user, I've been waiting > > desperately for a solution to this serious language flaw for 7 years. > > We've finally got a solution that works, and now folks want to hold it > > up for perhaps another year or more to solve a relatively minor problem? > > Grrr. > > I couldn't agree more. Since neither Bob nor Pascal were present at Tuck's briefing to the SIGAda attendees, let me set some context. Tuck did not pose the question "Would you rather have the proposed solution now, or a 'perfect' solution in a year?" If he had, then maybe the audience's response would have been different. Rather, his intent was just to get a sense of whether what the ARG has been doing will meet the needs / expectations of typical users. The response, at least on the interdependent types AI, seemed pretty clear to me (and also apparently to WG9, although I was not present at the WG9 meeting). The people who cared expressed their opinions very cogently. Solving 99.9% of a problem is fine, but if the last .1% is perceived as important by customers, then either convince the customer that they're wrong, or else solve the problem. As for the "it will take a year to solve", my reaction is: so what. I don't see compiler vendors rushing to get this stuff implemented. In any event, unless there are some other problems with the AI (in which case there would be a delay anyway), the only issue is the choice of syntax for the context clause item. So even before the eventual final approval of the AI, people (users and implementors) have a pretty good idea of what the feature will look like. By the way, where is the current version of this AI? I looked at the web site http://www.ada-auth.org/cgi-bin/cvsweb.cgi/AIs/AI-00217.TXT but the latest version shown there is pretty old. ************************************************************* From: Randy Brukardt Sent: Tuesday, December 17, 2002 12:45 AM The AI we approved is "alternative 4" (or AI95-00217-04), so it is found in the file AI-30217.TXT. So the correct URL is: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/AIs/AI-30217.TXT We need to declare the others !No Action at some point, both to get them off of the agenda and to help avoid mistakes... ************************************************************* From: Tucker Taft Sent: Tuesday, December 17, 2002 10:59 AM I will certainly take some of the blame for this, because I didn't have ready arguments to justify not having some visibility on the type stub in a context clause. However, it was a surprise to me that this was the one proposal that received widespread negative response, whereas interfaces, object.operation, etc., all received almost unanimous positive support. The ..._Interface package "idiom" does seem like a reasonable solution to the "type Object is" approach. The question is whether the idiom can also be considered to resolve the other objection, because the _Interface packages are so short. People do like Ada's context clause, and I do find Java's inconsistent use of imports a bit weird. In Java, if you give the full name of a class (i.e. including its full package name prefix) where it is used, then you don't need an "import" clause for the class. Essentially a Java "import" clause for a class makes the class name directly visible (with no package prefix required), so it is closer to a "use" clause for the enclosing package, than a "with" clause for the class. By contrast, the fact that Ada's context clauses indicate all external references seems like a very good thing. There seem to be various options to solve this problem: 1) Encourage use of the "..._Interface" idiom. 2) Add some kind of new "with" clause for packages that does not create a semantic dependence on the package. Only such packages may be mentioned in type stubs. 3) Add a new kind of "with" clause for types, that does not create a dependence on the package containing the type, and allows a renaming as part of the "with", effectively introducing the simple type name as an incomplete type implicitly declared immediately within the compilation unit having the "with" clause. 4) Add a new kind of "with" clause for types that implicitly creates an "incomplete" package for every package mentioned in the "with type" clause. Such incomplete packages must not turn out to be renames (renames were a big bugaboo of the earlier attempts at "with type"). Number 4 is pretty much back to the old "with type", except perhaps we have learned that renaming is bad news. Number 1 is keeping what we have now. ************************************************************* From: Randy Brukardt Sent: Tuesday, December 17, 2002 1:02 PM Ben said: > As for the "it will take a year to solve", my reaction is: so what. I don't > see compiler vendors rushing to get this stuff implemented. Well, I think that's wrong. Tucker already implemented a version of it in their compiler. I've been planning to do so as soon as I get some time, because it is badly needed to fix some structure problems in the Claw GUI Builder. I thought Pascal had indicated that they would be doing it soon. So there does seem to be some "rush to implement it" This is probably the most important AI of anything proposed for the amendment; certainly in the top two or three. We needed a solution for it years ago (roughly 25 years ago, I think), and holding it up over some perceived lack is dubious at best. > In any event, unless there are some other problems with the AI (in which > case there would be a delay anyway), the only issue is the choice of > syntax for the context clause item. No, there is at least one other solution, which is to revert to alternative #3, which does not specify the completing unit in the type stub. I don't know if that would satisify people or make them more uncomfortable - but it certainly gets rid of the "hidden" "dependence" (which is neither). I do agree that moving the declaration doesn't work, simply because it is a declaration. Thus, its location controls its visibility. To try to move it to the context clause would be a disaster (new mechanism, we'd need new rules to allow private declarations and rep. clauses and on and on and on.) In any case, we can at least take one meeting out of the cycle by coming to some sort of consensus here, and writing that proposal up beforehand (as alternative #5, I suppose). ************************************************************* From: Randy Brukardt Sent: Tuesday, December 17, 2002 1:19 PM > There seem to be various options to solve this problem: > > 1) Encourage use of the "..._Interface" idiom. > 2) Add some kind of new "with" clause for packages that does > not create a semantic dependence on the package. > Only such packages may be mentioned in type stubs. You forgot: 2A) (should be three, but I'm not going to renumber the others): Move the dependence to the completor (as in alternative #3). (I always objected to the "magic" semantics of the name in the stub.) The problems with that proposal (which seemed minor) would have to be revisited. (As I recall, the primary reason that this alternative was abandoned was that some ARG members didn't like the lack of indication about where the expected completion was. > 3) Add a new kind of "with" clause for types, that does > not create a dependence on the package containing the type, > and allows a renaming as part of the "with", effectively > introducing the simple type name as an incomplete type > implicitly declared immediately within the compilation unit > having the "with" clause. > 4) Add a new kind of "with" clause for types that implicitly > creates an "incomplete" package for every package mentioned > in the "with type" clause. Such incomplete packages must > not turn out to be renames (renames were a big bugaboo of > the earlier attempts at "with type"). #3 and #4 are way over the top; I see no reason to think about them further. That leaves us with the other three. The main question is whether #2A helps the problem, or makes people uncomfortable in the different way. And the answer to that question really drives the choice: if people really want a dependency to be declared (at the top) of the unit containing the stub, then no solution other than #2 can be used. (That seems to be Ben's position.) If the answer is that people are concerned about hidden dependencies, but are not bothered by the lack of a dependency, then #2A should be pursued. And if answer is that the majority of people want this now and don't care that much about it, then we should stick to #1. As far as the naming question goes, Pascal is right, we did discuss that in Cupertino. And we got the wrong answer IMHO. I don't see that allowing (or better still, requiring) type names in the stub makes the proposal any more complicated at all. And I know that conflicting type names are quite common, and I don't much like the answer of "don't do that!". But since I voted for that feature (and was outvoted), I don't get to reopen it. :-) ************************************************************* From: Robert A. Duff Sent: Tuesday, December 17, 2002 1:19 PM > Tucker already implemented a version of it in > their compiler. It was Sheri Bernstein, actually. I believe that implementation is "incomplete". I think having the final proposal nailed down would make vendors less reluctant to implement it. And if we all implement the same thing, that would be a Good Thing for the Ada community. ;-) >... I've been planning to do so as soon as I get some time, > because it is badly needed to fix some structure problems in the Claw GUI > Builder. I thought Pascal had indicated that they would be doing it soon. So > there does seem to be some "rush to implement it" > > This is probably the most important AI of anything proposed for the > amendment; certainly in the top two or three. Yeah, I'd say it's second only to the "abstract interfaces" thing. > In any case, we can at least take one meeting out of the cycle by coming to > some sort of consensus here, and writing that proposal up beforehand (as > alternative #5, I suppose). Sounds good to me. Perhaps our esteemed chair will pester us enough so that when the vote comes in Padua, we'll all know ahead of time how it's going to turn out. ;-) ************************************************************* From: Michael F. Yoder Sent: Tuesday, December 17, 2002 1:59 PM Disclaimer: I've never had a need for the feature under discussion, and so I'm speaking from a "what if" perspective here; and from the user's point of view, not the implementer's. My first reaction is, the objection as presented actually doesn't seem so unreasonable. But then I've not been expending bodily fluids on behalf of the proposal. :-) My second is, that option 2 is preferable to the others. As a wickerman proposal, how about with type in P1, ..., Pn; ************************************************************* From: Dan Eilers Sent: Tuesday, December 17, 2002 2:33 PM Tuck wrote: > There seem to be various options to solve this problem: > ... > 2) Add some kind of new "with" clause for packages that does > not create a semantic dependence on the package. > Only such packages may be mentioned in type stubs. > ... There seems to be a potential orthogonality with AI 262 (private with), if a user wants to indicate that the with'd unit can only be mentioned in type stubs, and only in the private part. ************************************************************* From: John Barnes Sent: Tuesday, December 17, 2002 10:39 AM >>...I >> want to get a resolution on this issue at the Padua meeting, so that >> we can send this AI (which I view as very important) to WG9 in June. My view is that we don't *have* to do anything. It would be quite reasonable for us to say that we have given it deep reconsideration and have concluded that it is best the way it is. Which is probably true. The United Kingdom would be happy with that conclusion I am sure. ************************************************************* From: Jean-Pierre Rosen Sent: Wednesday, December 18, 2002 1:42 AM > My second is, that option 2 is preferable to the others. As a wickerman > proposal, how about > > with type in P1, ..., Pn; Actually, I proposed the same idea to Tuck in Houston, but with the syntax: with package abstract Pack is type T1; type T2; end package; (only incomplete type declarations allowed, nothing else). You can view this either as: - A revival of the the abstract package, without creating new kind of compilation units - JAS (Just Another Syntax) for the type stub (same effect as "type T1 is separate in Pack", just appearing in another place) - Forcing a (partial) source compilation model on every compiler :-) ************************************************************* From: Pascal Leroy Sent: Wednesday, December 18, 2002 3:49 AM > As for the "it will take a year to solve", my reaction is: so what. I don't > see compiler vendors rushing to get this stuff implemented. In any event, > unless there are some other problems with the AI (in which case there would > be a delay anyway), the only issue is the choice of syntax for the context > clause item. So even before the eventual final approval of the AI, people > (users and implementors) have a pretty good idea of what the feature will > look like. I completely disagree with this analysis. First, any time spent by the ARG on 217 is time that is not spent on other AIs. So the question is as usual one of opportunity cost: what AIs are currently on the table that we want to bury for the sole purpose of marginally improving 217? Second, you have to remember that we are not doing this intellectual exercise in a vacuum: we have a schedule imposed by WG9, and this schedule calls for finalizing proposals a year from now. The worst that could happen is that revisiting 217 would either cause the entire revision to slip, or cause 217 to not be included in the amendment. Third, your comment about vendors is misguided, and may represent the position of your employer, but not that of other vendors. Speaking for Rational, we had a plan to start implementing some of the amendment AIs in the next few months, for inclusion in a release that will reach customers around the end of 2003, and 217 was part of that effort. The fact that this AI is now back on the drawing board means that we will probably cancel this effort (unless the Padua meeting clarifies the situation) so support for 217 in our products will slip to mid-2005. Who knows what IBM will decide is the right thing to do in that time frame? ************************************************************* From: Pascal Leroy Sent: Wednesday, December 18, 2002 3:56 AM > There seem to be various options to solve this problem: > > 1) Encourage use of the "..._Interface" idiom. > 2) Add some kind of new "with" clause for packages that does > not create a semantic dependence on the package. > Only such packages may be mentioned in type stubs. > 3) Add a new kind of "with" clause for types, that does > not create a dependence on the package containing the type, > and allows a renaming as part of the "with", effectively > introducing the simple type name as an incomplete type > implicitly declared immediately within the compilation unit > having the "with" clause. > 4) Add a new kind of "with" clause for types that implicitly > creates an "incomplete" package for every package mentioned > in the "with type" clause. Such incomplete packages must > not turn out to be renames (renames were a big bugaboo of > the earlier attempts at "with type"). I think stylistically #1 is a good idea regardless of what language features we add. I could go with #2 if we can quickly agree on a syntax/semantics and discuss that at Padua. If we want to allow renaming (which I believe is unnecessary because of #1) this must be done in the type stub declaration, not in the with clause as #3 seems to imply. The with clause must only be used to reference the package, not the type. But I'd rather not reopen the renaming issue since it was extensively discussed at Cupertino. #4 is effectively the old "with type", but if you reread the minutes of the meetings where this proposal was discussed, you'll see that renaming was only one of the many problems with this idea. One of the things that we wanted to do with "with type" was to allow importing of access types (to avoid having many different access types and conversions all over the place). But then the representation items associated with access types were problematic. And there were issues associated with the non-transitivity of with clauses, which caused ripple effects in semantic dependencies. ************************************************************* From: Pascal Leroy Sent: Wednesday, December 18, 2002 4:07 AM > You forgot: > > 2A) (should be three, but I'm not going to renumber the others): Move the > dependence to the completor (as in alternative #3). (I always objected to > the "magic" semantics of the name in the stub.) The problems with that > proposal (which seemed minor) would have to be revisited. (As I recall, the > primary reason that this alternative was abandoned was that some ARG members > didn't like the lack of indication about where the expected completion was. If you look at the history of 217/03 (funny how we seem to forget all these issues that we discussed to death), you'll see that there were two main problems: 1 - How do you specify that a full type completes a stub? (We considered syntax that would make the full type declarations even more complicated, or representation-item-like syntax, but none of that was satisfactory). 2 - You need to deal with the situation where a stub has several completions. This requires a lot of additional post-compilation checking, and errors may end up being detected very late in the development process. I find it curious that the users are telling us "we want to be more explicit/safe in specifying which unit contains the completion" and #2A answers that objection by saying "we won't let you specify where the completion lives" ;-) ************************************************************* From: Pascal Leroy Sent: Wednesday, December 18, 2002 4:12 AM > My first reaction is, the objection as presented actually doesn't seem > so unreasonable. But then I've not been expending bodily fluids on > behalf of the proposal. :-) The objection is not unreasonable, but the question is: is it important enough to reopen the AI? If all we do is quickly agree on the syntax/semantics of a new kind of with clause, fine. But the recent mail traffic has shown that we were reopening all sorts of all discussions, and that's the best way to lose a year. > My second is, that option 2 is preferable to the others. As a wickerman > proposal, how about > > with type in P1, ..., Pn; I am rather sick and tired with the combination of words "with type" but other than that I am not going to argue about the syntax. We'll have a straw vote anyway. ************************************************************* From: Pascal Leroy Sent: Wednesday, December 18, 2002 4:13 AM > There seems to be a potential orthogonality with AI 262 > (private with), > if a user wants to indicate that the with'd unit can only be mentioned > in type stubs, and only in the private part. Good point, and an illustration that Nothing Is Simple. ************************************************************* From: Ben Brosgol Sent: Wednesday, December 18, 2002 10:00 AM > Second, you have to remember that we are not doing this intellectual > exercise in a vacuum: we have a schedule imposed by WG9, and this schedule > calls for finalizing proposals a year from now. The worst that could happen > is that revisiting 217 would either cause the entire revision to slip, or > cause 217 to not be included in the amendment. Part of the non-vacuum is the user community (i.e., the real customers of the Amendment work), and indeed one of the purposes of Tucker's presentation was to elicit a response to the AIs. What was the sense of doing the briefing if we are going to complain that we didn't like the response? :-) The feeling that I got from the discussion of 217 (John, Tucker, Jean-Pierre and maybe some others on this list were there so they can correct me if they disagree) was that a number of individuals strongly felt that a lack of something on the context clause was a violation of Ada design principles. It also seemed that this could be addressed with a simple syntactic addition. (OK, getting agreement on the right syntax is maybe not so simple :-) I did not get the feeling from the discussion that this required re-opening the AI for general surgery but maybe I'm being naive. Some of the suggestions that have appeared on this thread, e.g. "with type in P1, P2, ...;" combined with alternative 2 seem to solve the problem. It does not address the issue of the same type stub name appearing in more than one "with type"d package. But there are workarounds that may be satisfactory. The interdependent types issue seems to be one of the more important items for the Amendment work, so I would be a bit surprised if this AI simply gets dropped based on its rejection in its current form. ************************************************************* From: Dan Eilers Sent: Wednesday, December 18, 2002 12:56 PM I'd like to propose a solution to AI 217 based on the _Interface package idea. Pascal wrote: > The AI explains (in the !example section) that it is advisable to introduce an > _Interface package to declare the type stubs. The reason is that if you have > types that are involved in more than one cycle, this provides the best > structure. > > With this approach, the example from the AI works fine even if everything is > named Object: > > package Employees_Interface is > type Object is tagged separate in Employees; > type Emp_Ptr is access all Object'Class; > end Employees_Interface; > > package Departments_Interface is > type Object is tagged separate in Departments; > type Dept_Ptr is access all Object'Class; > end Departments_Interface; > > with Departments_Interface, Employees_Interface; > package Employees is > type Object is tagged private; > procedure Assign_Employee(E : in out Object; > D : in out Departments_Interface.Object); > ... > function Current_Department(D : in Object) return > Departments_Interface.Dept_Ptr; > end Employees; > > with Departments_Interface, Employees_Interface; > package Departments is > type Object is tagged private; > procedure Choose_Manager(D : in out Object; > Manager : in out Employees_Interface.Object); > ... > end Departments; Instead of "Employees_Interface", what you'd really like to write is "Employees.Interface", making explicit the connection to package Employees. But this doesn't work because of the rule that disallows declaring a child unit before its parent has been compiled (and you can't compile the parent first due the circular dependencies). So I propose to relax this rule, with some new syntax to be explicit what's going on. The proposed syntax is that a child unit can be declared using a tick mark in place of the period, to indicate that its parent need not previously have been compiled. References would still use a period. Rewriting the above example: package Employees'Interface is -- new syntax type Object is tagged separate in Employees; type Emp_Ptr is access all Object'Class; end Employees'Interface; package Departments'Interface is type Object is tagged separate in Departments; type Dept_Ptr is access all Object'Class; end Departments'Interface; with Departments.Interface, Employees.Interface; package Employees is type Object is tagged private; procedure Assign_Employee(E : in out Object; D : in out Departments.Interface.Object); ... function Current_Department(D : in Object) return Departments.Interface.Dept_Ptr; end Employees; with Departments.Interface, Employees.Interface; package Departments is type Object is tagged private; procedure Choose_Manager(D : in out Object; Manager : in out Employees.Interface.Object); ... end Departments; This not only makes the _Interface package cleaner, but it eliminates the need for any special kind of "with" clause, and eliminates the objectionable reference in type stubs to packages that haven't been mentioned in a context clause. Note that Interface as used here isn't special, its just a user-defined child unit name. Being able to declare a child unit before its parent has been compiled may have uses as well (I think someone has asked for this before). ************************************************************* From: Robert A. Duff Sent: Wednesday, December 18, 2002 1:20 PM > Part of the non-vacuum is the user community (i.e., the real customers of > the Amendment work), and indeed one of the purposes of Tucker's presentation > was to elicit a response to the AIs. What was the sense of doing the > briefing if we are going to complain that we didn't like the response? :-) Good point. The ARG *should* be listening to the user community, as best it can. (We don't have a large budget for market studies...) > The feeling that I got from the discussion of 217 (John, Tucker, Jean-Pierre > and maybe some others on this list were there so they can correct me if they > disagree) was that a number of individuals strongly felt that a lack of > something on the context clause was a violation of Ada design principles. And they are correct (although I can't get too excited about this issue). > It also seemed that this could be addressed with a simple syntactic > addition. It could be, but as you can see from the discussion so far, all kinds of new (and not-so-new) proposals come out of the woodwork. These new ideas, and perhaps even rehashed old ideas, may have merit, but I think it's a mistake to waste time studying them when we have a proposal that basically works (after 7 years work so far!). I suggest we stick to solving the exact problem, and no more, by adding a new kind of with clause with no more semantics than "herein lie type stubs pointing at package X". If it works, we should agree on the syntax via e-mail before the next meeting. If not, we should stick to the current proposal. Either way, the vote at the next meeting should be a formality. Wishful thinking? ************************************************************* From: John Barnes Sent: Wednesday, December 18, 2002 1:51 PM Absolutely right. Nonody at that presentation wanted anything fancy. just a mark to say "herein lie stubs in package X". I thought my suggestions of with out X; was just the job. But there the world is a dull place. So maybe echoing Bobs phrase we could something more elaborate such as with in is separate at package X; At Christmas on we can be generaous with reserved words. Happy Holidays ************************************************************* From: David Emery Sent: Wednesday, December 18, 2002 2:14 PM >... >with in is separate at package X; Reminds me of the (pornographic) story Norm Cohen once wrote using -only- reserved words. ************************************************************* From: Michael Yoder Sent: Wednesday, December 18, 2002 2:02 PM Robert A Duff wrote: >I suggest we stick to solving the exact problem, and no more, by adding >a new kind of with clause with no more semantics than "herein lie type >stubs pointing at package X". If it works, we should agree on the >syntax via e-mail before the next meeting. If not, we should stick to >the current proposal. Either way, the vote at the next meeting should >be a formality. Wishful thinking? I agree. Going even further, can we choose a single person (I'll suggest Tucker) to just pick a syntax they like? I'm willing to rubber-stamp in advance whatever you guys come up with, insofar as that helps expedite email discussions. ************************************************************* From: Robert A. Duff Sent: Wednesday, December 18, 2002 2:02 PM Sounds like a good plan to me. I'd even be willing to draw straws to see who gets to pick the syntax. ************************************************************* From: Robert Dewar Sent: Wednesday, December 18, 2002 6:09 PM > Sounds like a good plan to me. I'd even be willing to draw straws to > see who gets to pick the syntax. Not me. I think the exact choice of syntax can be a significant issue. I would like to recall the furious acrimony surrounding the basically not very important distinction between type x is tagged record ... and class x is record ... :-) ************************************************************* From: Randy Brukardt Sent: Wednesday, December 18, 2002 6:46 PM And as further proof, see Erhard's reaction to the syntax proposed. Still, determining the complexity of the semantics is probably the most important for ARG purposes. The syntax can pretty much be changed at any time. ************************************************************* From: Ed Schonberg Sent: Wednesday, December 18, 2002 2:14 PM > I thought my suggestions of > > with out X; > > was just the job. The pun is confusing at first reading. Why not: with in X; Can't be any shorter! ************************************************************* From: Randy Brukardt Sent: Wednesday, December 18, 2002 2:12 PM > I suggest we stick to solving the exact problem, and no more, by adding > a new kind of with clause with no more semantics than "herein lie type > stubs pointing at package X". If it works, we should agree on the > syntax via e-mail before the next meeting. If not, we should stick to > the current proposal. Either way, the vote at the next meeting should > be a formality. Wishful thinking? I agree completely. Indeed, I was going to do just that if no one else beat me to it: create an alternative 5 that's the same as alternative 4 with the addition of this syntax (including the wording), and then we can choose between them. Of course, if someone else does it before I do, I won't mind -- I've got plenty of other ARG work to do with my limited time and budget. Syntax-wise, I think I like Bob Duff's original suggestion (way back at 8am yesterday) of with separate P; which, as Dan points out, will need a private option: [private] with separate P; ************************************************************* From: Dan Eilers Sent: Wednesday, December 18, 2002 2:50 PM > which, as Dan points out, will need a private option: > [private] with separate P; > > Randy. Please don't credit/blame me for the "[private]" in this proposal. My purpose in pointing it out was exactly the opposite, by showing that adding a new kind of with clause to the AI 217 proposal is not as simple as was originally thought. And it is totally unnecessary as well, since type stubs are most logically placed in a child unit, and child units don't need with clauses for their parents. The only change needed to AI 217 is changing the underscore in "Employees_Interface" to a dot or tick mark. ************************************************************* From: Robert A. Duff Sent: Wednesday, December 18, 2002 3:30 PM But you can't "with" a child unit without also "with"-ing its parent. Clients need to depend upon the type stubs without depending on the "real" type. ************************************************************* From: Dan Eilers Sent: Wednesday, December 18, 2002 3:42 PM Yes, I understand that in Ada as it currently exists, a with clause on child unit implicitly creates a dependency on its parent. But this is because the child unit itself implicitly depends on its parent. My proposal was to break this implicit dependence between a child unit and its parent (using a tick mark to distinguish). Once that dependence is broken, it is perfectly fine to "with" a child unit without also implicitly "with"-ing its parent. I proposed retaining the dotted notation in the with clause, but maybe using the tick mark in the with clause would make it more clear to the reader and the compiler that there is no implicit dependence on the child's parent. ************************************************************* From: Jean-Pierre Rosen Sent: Thursday, December 19, 2002 1:41 AM From: "Robert A Duff" > But you can't "with" a child unit without also "with"-ing its parent. > Clients need to depend upon the type stubs without depending on the > "real" type. Of course you can: with parent.child package Parent_Child renames parent.child; with Parent_Child; -- No dependence to Parent .... ************************************************************* From: Robert A. Duff Sent: Thursday, December 19, 2002 10:39 AM Semantic dependence is transitive (10.1.1(26)), so the above comment is not correct. And from an implementation point of view, it has to be that way, because names in the "...." unit can refer into the symbol table of Parent. ************************************************************* From: Robert Dewar Sent: Friday, December 20, 2002 5:54 PM Most certainly correct, nevertheless, JPR's renaming is interesting in that it shows you can have visibility of the child without visibility of the parent if I understand things correctly. ************************************************************* From: Robert A. Duff Sent: Friday, December 20, 2002 8:04 PM Yes, quite true. But you can still access things that are not directly visible: package Parent is subtype S1 is Integer; subtype S2 is Integer; end Parent; package Parent.Child is X: S1; Y: S2; end Parent.Child; with Parent.Child; package Mumble renames Parent.Child; with Mumble; procedure Main is begin Mumble.X := Mumble.Y; Clearly the compiler needs to know about Parent in order to check whether the above assignment is legal (and to generate code for it). The name Parent is not visible in Main, but Main still semantically depends upon Parent. ************************************************************* From: Gary Dismukes Sent: Wednesday, December 18, 2002 5:19 PM > I agree completely. Indeed, I was going to do just that if no one else beat > me to it: create an alternative 5 that's the same as alternative 4 with the > addition of this syntax (including the wording), and then we can choose > between them. > ... > Syntax-wise, I think I like Bob Duff's original suggestion (way back at 8am > yesterday) of > with separate P; > which, as Dan points out, will need a private option: > [private] with separate P; I'm in favor of that basic approach as well, with exact syntax deserving of a little more (e-mail) discussion. I can also see some merit in Mike Yoder's suggestion (with type in ...) since it indicates that it's the types that are of interest from the named package(s). ************************************************************* From: Erhard Ploedereder Sent: Wednesday, December 18, 2002 5:37 PM My initial reaction was/is to stick with the AI as is. (And I think I can make it the German reaction at WG9.) I believe we were blindsided at the meeting by nothing more than a gut reaction by the users who had all but 10 minutes to get familiar with the model. In terms of argument, consider: with XYZ; -- creates a semantic dependence with private ABC; -- creates a semantic dependence with funnykeyword PKG; -- does not create a semantic dependence What kind of semantic consistency is that ?? And what benefit by the added with-clause (without other changes) ? The following two lines are semantically equivalent (I hope): (1) with funnykeyword PKG; (2) -- with funnykeyword PKG; Based on that, I believe that adding a with clause should be rejected. ---------- but if people do insist on a change.... I can't resist adding a proposal for another context_item from GHL use type T; -- does not create a semantic dependence on GHL; -- allows "T" and "GHL.T" in places were the names -- of incomplete types are allowed; etc., etc. -- regarding completion checks and then revoke the syntax change to incomplete type decls. (Note that this also happens to allow for a solution of the problem of equal names, if instead only "GHL.T" is allowed.) Compared to the current solution, one loses the ability to "export" the incomplete type, but that may be a good thing, anyway. For now, I would insist that whatever syntax is invented, it not start with a "with". ************************************************************* From: Robert Dewar Sent: Wednesday, December 18, 2002 5:40 PM > > The feeling that I got from the discussion of 217 (John, Tucker, Jean-Pierre > > and maybe some others on this list were there so they can correct me if they > > disagree) was that a number of individuals strongly felt that a lack of > > something on the context clause was a violation of Ada design principles. Count me among this "number of individuals" :-) ************************************************************* From: Erhard Ploedereder Sent: Wednesday, December 18, 2002 5:59 PM > Syntax-wise, I think I like Bob Duff's original suggestion (way back at 8am > yesterday) of > with separate P; > which, as Dan points out, will need a private option: > [private] with separate P; Please DON'T, since this comes close to mixing properties of P (which we are not supposed to depend on) into the semantics of the clause, doesn't it? Which gives me yet another argument against the "with"-clause strategy... private package Parent.Daughter is type T is.... end Parent.Daughter; package Parent.Son is type T2 is.... end Parent.Son; with separate Parent.Son; -- ok with separate Parent.Daughter; -- is this ok ??? for normal "with" it isn't. package foo is Or have we uncovered a bug in the original AI here, making it possible to violate privacy of private child packages ? (At first glance, I don't think so, because ops on the type are subject to visibility.) ************************************************************* From: Randy Brukardt Sent: Wednesday, December 18, 2002 6:43 PM As you say, "with Parent.Daughter;" is illegal in Ada 95. That's the problem that "private" with is intending to fix. "private with Parent.Daughter;" is legal. So I would say that "with separate Parent.Daughter;" ought to be illegal as well. But you are right, we don't have any way to tell that (no dependency on the unit). So we can't make it illegal. > Or have we uncovered a bug in the original AI here, making it possible > to violate privacy of private child packages ? (At first glance, I don't > think so, because ops on the type are subject to visibility.) I think that you may be right, that AI-217 has a problem here. package Foo is type T is separate in Parent.Daughter; end Foo; seems to be legal. And it essentially allows Parent.Daughter.T to be accessed in unrelated packages. OTOH, I don't think you can do anything useful with it (because, as you say, you don't have visibility on the full definition unless you can give a context clause for Parent.Daughter). Still it seems like exporting something that you can't legitimately have access on. ************************************************************* From: Robert Dewar Sent: Wednesday, December 18, 2002 8:19 PM > This seems like, how does that saying go, "perfection is the enemy of > the good", or something like that? Technically, the objection is right: > there *should* be some mention of that package in the context clause. > But from a practical point of view: As an Ada user, I've been waiting > desperately for a solution to this serious language flaw for 7 years. > We've finally got a solution that works, and now folks want to hold it > up for perhaps another year or more to solve a relatively minor problem? > Grrr. I strongly object to any rushing here. The fact of the matter is that the WG9 approving an AI does not change the standard and suddently require all compiler writers to go out and implement a feature anyway, we are possibly talking about a new version of Ada for 2005, that vendors may or may not implement at that time. If you want to add some feature to your compiler before there is a new standard feel free, but it is absolutely not acceptable to rush anything. There is simply nothing that urgent. ************************************************************* From: Robert Dewar Sent: Wednesday, December 18, 2002 8:28 PM > Well, I think that's wrong. Tucker already implemented a version of it in > their compiler. I've been planning to do so as soon as I get some time, > because it is badly needed to fix some structure problems in the Claw GUI > Builder. I thought Pascal had indicated that they would be doing it soon. So > there does seem to be some "rush to implement it" Anyone is free to implement anything, and indeed trial implementations are useful. For GNAT, we find our implementation of with type good enough for now. ************************************************************* From: Robert Dewar Sent: Wednesday, December 18, 2002 8:55 PM > I completely disagree with this analysis. > > First, any time spent by the ARG on 217 is time that is not spent on other > AIs. So the question is as usual one of opportunity cost: what AIs are > currently on the table that we want to bury for the sole purpose of > marginally improving 217? But if we go to WG9 and say "well this is not perfect, and it could be improved, but we are in a hurry and don't have time to do it right." I think the result will be that WG9 rejects it. That won't save time! ************************************************************* From: Robert Dewar Sent: Wednesday, December 18, 2002 8:56 PM > > There seems to be a potential orthogonality with AI 262 do you mean non-orthogonality here? Orthonogal features are ones that do not interact in a negative way (the terms was introduced I think perhaps by Bauer in the context of the Algol-68 design). ************************************************************* From: Ben Brosgol Sent: Wednesday, December 18, 2002 11:51 PM > ---------- but if people do insist on a change.... ... snip ... > For now, I would insist that whatever syntax is invented, it not start > with a "with". How about: for type goto P; or maybe more seriously: at P; ************************************************************* From: Pascal Leroy Sent: Thursday, December 19, 2002 1:41 AM > ---------- but if people do insist on a change.... > > I can't resist adding a proposal for another context_item > from GHL use type T; -- does not create a semantic dependence on GHL; > -- allows "T" and "GHL.T" in places > were the names > -- of incomplete types are allowed; etc., etc. > -- regarding completion checks > > and then revoke the syntax change to incomplete type decls. > (Note that this also happens to allow for a solution of the problem > of equal names, if instead only "GHL.T" is allowed.) Can you explain to me how this differs from the original "with type" proposal? I may be missing something, but I see no point in resurrecting proposals that have been discussed ad nauseam and discarded for good reasons. ************************************************************* From: Tucker Taft Sent: Thursday, December 19, 2002 8:43 AM Robert Dewar wrote: >>>There seems to be a potential orthogonality with AI 262 >> > > do you mean non-orthogonality here? > > Orthonogal features are ones that do not interact in a negative way (the > terms was introduced I think perhaps by Bauer in the context of the Algol-68 > design). The "orthogonality" that comes to my mind based on Dan's statement is as follows: If "private with X;" makes the with clause only applicable in the private part, then "separate with X;" make the with clause only applicable in type stubs. And I suppose "private separate with X;" makes the with clause only applicable in type stubs in the private part. From a syntax point of view, if we have gotten used to the idea of "private with ..." I would recommend we stick with putting the qualifier at the front of the with clause, so as implied above I recommend the syntax be: [private] [separate] with library_unit_name {, library_unit_name}; I am also intrigued by Dan's 'Interface suggestion which is very similar to the package "abstract" proposal, but... The biggest problem I have is keeping straight in my mind the various difficulties associated with each of the alternative proposals we have considered. Pascal reminded me of a few. Here are some ones I can remember: 1) Problems with package abstracts a) New kind of compilation unit could be major disruption to tools b) Not clear what kinds of declarations may appear in package abstract c) Must everything be repeated in package spec, or is package abstract just a prefix on the package spec? d) Not obvious in package spec whether package abstract does or does not exist (bad memories of optional package bodies and optional subprogram specs) 2) Problems with with-type a) Want to share access type, but no way to import various rep clauses b) Context clause visibility is not transitive, so this complicates type compatibility rules c) Renaming created type compatibility snafoos 3) Problems with type stubs a) Nothing in context clause to signal presence of stub. b) Doesn't work as well when all types have the same simple name (e.g. type Object is ...) I'm sure there were others. It would be nice to have a well-specified list of these problems, with possible solutions suggested, and then we might be able to make a rational selection among them. ************************************************************* From: Erhard Ploedereder Sent: Thursday, December 19, 2002 10:20 AM I can add: 4) Problems with "full decl identifies stub": a) there can be multiple stubs for the same full decl (should be supported; but even if declared illegal, seems rather difficult to check) b) there may be erroneously multiple full decls for a single stub (must be illegal, but not easy to check) c) would presumably run afoul the same critique, in even worse ways, as the "type stub identifies pkg" model. And I believe I can add a "solution" to problem 2 a), as outlined earlier in another message: The thus "postulated" type is to be treated as an incomplete type, so properties of the full type are irrelevant. All proposals that resort to the incomplete type model share the problem of incompatible access types formed over multiple views of the type. (Possible solution lies in user-namable anonymous access types. :-) ************************************************************* From: Erhard Ploedereder Sent: Thursday, December 19, 2002 10:49 AM > from GHL use type T; -- does not create a semantic dependence on GHL; > -- allows "T" and "GHL.T" in places > were the names > -- of incomplete types are allowed; etc., etc. > -- regarding completion checks > Can you explain to me how this differs from the original "with type" > proposal? I may be missing something, but I see no point in ... While "with type" tried to import full-fledged access types, this one makes the imported type incomplete, and hence representation is irrelevant. For all intents and purposes, all I did was to move the incomplete type decl into the context clause. Apart from the difference that now no name for the incomplete type is exported anymore (which I consider an advantage to prevent the "poisoning" of other modules with incomplete types -- if they want it, they can import it, too), and the possibility of solving the homograph problem, it is semantically not different from the type stub model. As to problems, using Tuck's list for "with type": a) Want to share access type, but no way to import various rep clauses -- no problem, since we do not import access types as such b) Context clause visibility is not transitive, so this complicates type compatibility rules -- This problem remains and could be solved by a rule that "pgk.T = pkg.T" (presuming one goes the route of mandating selected notation). Not a nice rule, since text-based. Allowing both "T" and "pkg.T" is rather deadly in this regard. c) Renaming created type compatibility snafoos -- none such allowed, since you can't subtype incomplete types ************************************************************* From: Erhard Ploedereder Sent: Thursday, December 19, 2002 10:50 AM On the context clause proposal, I have yet another syntax: "not with P;" :-) This nicely expresses the fact that no dependency is formed and yet the package has been mentioned as desired. ************************************************************* From: Robert A. Duff Sent: Thursday, December 19, 2002 10:55 AM Tucker wrote: > [private] [separate] with library_unit_name {, library_unit_name}; That makes sense. > 3) Problems with type stubs > a) Nothing in context clause to signal presence of stub. > b) Doesn't work as well when all types have the same simple name > (e.g. type Object is ...) > > I'm sure there were others. It would be nice to have a > well-specified list of these problems, Isn't that information in the AI's? >... with possible solutions > suggested, and then we might be able to make a rational > selection among them. I was under the impression that we have already done that, and concluded that type stubs are the best solution. So I suggest we address the above problems, and be done with it: 3 a) Add "separate with lib_unit" syntax as above, and a single legality rule. Change the wording about with clauses, so this new kind doesn't cause semantic dependence. 3 b) The suggested coding style does not have this problem, so it's not a real problem. The AI should point this out. ************************************************************* From: Pascal Leroy Sent: Thursday, December 19, 2002 11:23 AM > I was under the impression that we have already done that, > and concluded that type stubs are the best solution. > So I suggest we address the above problems, and be done with it: > > 3 a) Add "separate with lib_unit" syntax as above, and a > single legality > rule. Change the wording about with clauses, so this new kind doesn't > cause semantic dependence. > > 3 b) The suggested coding style does not have this problem, > so it's not a real problem. The AI should point this out. I like that approach and I would hope that we could go that way. Unfortunately, many people in this discussion seem to want to reopen old cans of worms. ************************************************************* From: Dan Eilers Sent: Thursday, December 19, 2002 11:45 AM I think it's fair to say that we have pretty conclusively ruled out "with type" and "package abstracts" as originally proposed, and are focused on variations of type stubs. "With type" is simple to implement, but doesn't give full needed functionality. "package abstracts" are clean from a user perspective, but are too heavy (new type of compilation unit, new type of with clause). The issue of type stubs came up for reconsideration as the result of valid WG9 concerns that it goes against Ada design principles to reference an unknown package in a type stub. One solution to the WG9 concern is to weigh down the type-stub proposal with a new sort of with-clause. But this is heavy, and was one of the main reasons for shooting down package abstracts. It's even heavier than originally thought, due to orthogonality concerns with the private-with proposal. To lighten the amended type-stub proposal, there have been at least two suggestions that haven't been previously considered, and that haven't yet been shot down: 1) Erhard's proposal that the type stub may not be needed/wanted if you essentially move it to the context clause. 2) My proposal that the new with-clause isn't needed if you relax the rule about child units always implicitly depending on their parents. (This gets the user benefits of package abstracts without the heaviness of adding a new type of compilation unit or new with clause.) So I don't agree that the type-stub issue is closed yet. ************************************************************* From: Tucker Taft Sent: Thursday, December 19, 2002 12:42 AM >>I'm sure there were others. It would be nice to have a >>well-specified list of these problems, > > > Isn't that information in the AI's? Yes, I presume it is there. My problem is keeping it all in my head at one time, so that I don't swing wildly from one idea to another just because I forgot all the problems with each alternative. Getting them all centralized into a single cohesive list would certainly help me. The information in the AIs is a bit too spread out. >>... with possible solutions >>suggested, and then we might be able to make a rational >>selection among them. > > > I was under the impression that we have already done that, > and concluded that type stubs are the best solution. Well, Erhard originally suggested the type stub, I believe, but he seems to be looking more favorably on something closer to "with type" now. It may be that by recommending the "_Interface" package idiom if there is a need to declare a shared access type, the "with-type" which only allows incomplete types (which I think is all the final version did) is worth reconsidering. One advantage of the "with type" is that it is *not* transitive, and it requires use of the full expanded name of the type, rather than only providing for use of a simple name. I think if we disallow mentioning renamed packages in a with-type clause, it is essentially equivalent to the type stub, but solves both problems associated with the type stub (context clause visibility, and "type Object is..."). ************************************************************* From: Erhard Ploedereder Sent: Thursday, December 19, 2002 11:45 AM > Well, Erhard originally suggested the type stub, I believe, but > he seems to be looking more favorably on something > closer to "with type" now. Actually, I am still in favor of the AI as written. But I am trying to swing with the punches, seeing that the ARG is being swayed, too, by the "must be part of context clauses" attitude. And getting the "type Object"-Problem solved alongside, well.... (I definitely do not want to see the abstract package solution. That one is way too heavy.) ************************************************************* From: Pascal Leroy Sent: Thursday, December 19, 2002 11:30 AM > > Can you explain to me how this differs from the original "with type" > > proposal? I may be missing something, but I see no point in ... > > While "with type" tried to import full-fledged access types, this one > makes the imported type incomplete, and hence representation > is irrelevant. When we discussed the "with type" idea a long time ago (I think it was at the Intermetrics meeting) the general feeling was that a mechanism for importing access types was fundamentally important. Otherwise you have to have multiple access types, and you end up with explicit conversions between access types everywhere in your code. So much for the legendary readability of Ada! And don't tell me that anonymous-access-types-everywhere solve that problem, because that AI (230, as I recall) is fraught with difficulties and would be awfully disruptive for compilers. ************************************************************* From: Randy Brukardt Sent: Thursday, December 19, 2002 6:10 PM > The biggest problem I have is keeping straight > in my mind the various difficulties associated > with each of the alternative proposals we have > considered. Pascal reminded me of a few. Here are > some ones I can remember: Here is the list I sent in July 2001 [found in the Appendix to alternative 3]. (We hadn't invented the current proposal at that time): "with type" Con: Can't handle access types (due to representation issues). Implementation needs "pseudo packages" internally. What happens when multiple paths to the same item "meet"? Pro: Easy to describe to users. "package abstract" Con: Heavy, complex mechanism. Complex visibility issues to work out. New kind of compilation unit affects all tools (alternatives avoiding compilation unit has similar effect on tools because the source must be compiled in two different ways). Pro: Handles access types cleanly. "type separate" Con: Must introduce extra packages (either "unnecessary" parents with Tucker's rules or "unrelated" ones with Dan's/Randy's rules). No allocators/deallocators on access types with designated types of this kind. (Probably others, once a proposal is made and picked apart.) Pro: No new kind of units. No new visibility issues. Access types can be handled (representation clauses can be given if needed). Model is easy to understand (it is an extension of existing rules). No run-time implementation problems; the implementation is identical to incomplete-completed-in-body. Comparing this to Tucker's list, they appear to be pretty much the same, so I think we can safely just use his list. ************************************************************* From: Tucker Taft Sent: Thursday, December 19, 2002 8:35 PM Well I went back and reread both the "with type" and the "type xxx is separate..." proposals, and it really seems like if we are willing to use the rules of "type XXX is separate ..." but with the syntax of "with type ..." we get the best of both worlds. In particular, much of the complexity of "with type ..." came from trying to support references to package renamings. If we simply disallow that, we can treat the enclosing package name roughly like a string literal, and not worry about it until a point where we need a full type. At that point we have to make sure that the enclosing package has been "with"ed. The matching rules given in the type stub proposal could be used directly. The access type concerns associated with "with type" are no worse than the type stub, since both can only be used to introduce an incomplete type. If you want a shared access type, then you create an _Interface (or _Pointers) package, and declare the access type there. I would disallow subtypes of the incomplete types (as is disallowed for incomplete types now), and disallow renamings of packages whose names are mentioned only in "with type" clauses. The rule given in the type-stub proposal for when the full type must be visible would work for the "with type" proposal just as well. Hence, I would propose that we go back to the with-type proposal, with semantic rules taken in large part from the type-stub proposal. ************************************************************* From: Pascal Leroy Sent: Thursday, December 19, 2002 11:43 PM > > I thought Pascal had indicated that they would be doing it soon. So > > there does seem to be some "rush to implement it" > > Anyone is free to implement anything, and indeed trial implementations > are useful. For GNAT, we find our implementation of with type > good enough for now. My company is not interested in implementing experimental features for the fun of it, because that's not what our customers want. They want features that solve their problems, will be supported over the next 10 years, and will be portable to other compilers. An endorsement by WG9 is (short of the production of a TC or an Amendment) a good indication that a proposed feature is likely to be supported by a variety of compilers over long periods of time. We have seen a lot of demand for a solution to the problem of mutually dependent types in the 95-98 timeframe, because people were under the impression that with Ada 95 they would be able to easily map UML models to Ada. That particular issue came up a lot, and was a nightmare in real projects. Since then the pressure has diminished, either because people have somehow circumvented the problem, or because they have moved to a language that supports mutually dependent types. Wait another year and we will have reached the point where it's impossible to make a business case for implementing type stubs. ************************************************************* From: Pascal Leroy Sent: Friday, December 20, 2002 1:48 AM > Well I went back and reread both the "with type" > and the "type xxx is separate..." proposals, and > it really seems like if we are willing to use > the rules of "type XXX is separate ..." but with > the syntax of "with type ..." we get the best > of both worlds. I am really annoyed that we are reopening the "with type" proposal, because there were many technical difficulties associated with it which I suppose we are going to re-discover. At some point in time we at Rational made a detailed design of how this could be implemented in our compiler, and came up with two very significant problems: 1 - In order to implement "with type" the compiler would have to build hidden package declarations to hold the incomplete types. The reason is that throughout the compiler we are making the assumption that types (incomplete or not) are only declared in declarative parts, not in the middle of context clauses, and hidden packages seem the best solution to avoid breaking this assumption. Interestingly enough I remember discussing this with Tucker privately during the Leuven meeting, and at the time Averstar had started a design too, and they had come with the exact same mechanism. This is in fact what led to the "package abstract" proposal: the minutes of the Leuven meeting have the following paragraph: "Tucker would like to abandon the with type proposal. He says it is misguided, because you have to posit the existence of "ghost" or "incomplete" packages to describe it. In other words, a compiler will need to create a hidden package specification, and populate it with the right stuff. Then it is possible to see a lot of these ghost packages, which provide different views of the same package, and the compiler must figure out that they all correspond." 2 - The consistency check between the incomplete type declaration(s) and the completion is practically impossible to implement for a compiler doing incremental compilation. The "with type" AI has the rule: "Any compilation unit that has a semantic dependence on another compilation unit which has a "with_type_clause," as well as a dependence on the package identified by the with_type_clause, must verify that a type declaration (not just a subtype declaration) for the named type does in fact appear within the visible part of the package." But this means that the addition of a with clause (either a normal one or a "with type") can change the location where the consistency check has to be performed. Similarly for the removal of an otherwise unused with clause. The impact analysis phase of our compiler would have to remember the entire dependency graph, and determine if, because of incremental changes, the locations where the check is made change. If so, it would have to obsolesce the appropriate units. Not only is that awfully complicated, but it has a complexity which is proportional to the size of the entire program, not to the size of the change, so it essentially kills incremental compilation. I believe that this issue illustrates the fact that the non-transitivity of with clauses is a drawback, not an advantage, of the "with type" model. > In particular, much of the complexity > of "with type ..." came from trying to support > references to package renamings. Curiously, that's not what I remember. This was only one of the numerous issues that killed that proposal. ************************************************************* From: Tucker Taft Sent: Friday, December 20, 2002 8:20 AM > "Tucker would like to abandon the with type proposal. He says it is > misguided, because you have to posit the existence of "ghost" or > "incomplete" packages to describe it. In other words, a compiler will need > to create a hidden package specification, and populate it with the right > stuff. Then it is possible to see a lot of these ghost packages, which > provide different views of the same package, and the compiler must figure > out that they all correspond." This problem is significantly simplified, I believe, if you don't allow the use of package renamings in the name of the with'ed type. > 2 - The consistency check between the incomplete type declaration(s) and the > completion is practically impossible to implement for a compiler doing > incremental compilation. The "with type" AI has the rule: > > "Any compilation unit that has a semantic dependence on another compilation > unit which has a "with_type_clause," as well as a dependence on the package > identified by the with_type_clause, must verify that a type declaration (not > just a subtype declaration) for the named type does in fact appear within > the visible part of the package." I suggested we use the rule from the type-stub proposal *instead* of this rule. It avoids the problems you had with this one, I believe. > But this means that the addition of a with clause (either a normal one or a > "with type") can change the location where the consistency check has to be > performed. Similarly for the removal of an otherwise unused with clause. > The impact analysis phase of our compiler would have to remember the entire > dependency graph, and determine if, because of incremental changes, the > locations where the check is made change. If so, it would have to obsolesce > the appropriate units. Not only is that awfully complicated, but it has a > complexity which is proportional to the size of the entire program, not to > the size of the change, so it essentially kills incremental compilation. > > I believe that this issue illustrates the fact that the non-transitivity of > with clauses is a drawback, not an advantage, of the "with type" model. > > >>In particular, much of the complexity >>of "with type ..." came from trying to support >>references to package renamings. > > > Curiously, that's not what I remember. This was only one of the numerous > issues that killed that proposal. In any case, if these are significant problems with the with-type, then they need to be put into our "concise" list of pros/cons. Somehow I missed them, and I believe Randy did as well. ************************************************************* From: Erhard Ploedereder Sent: Friday, December 20, 2002 8:33 AM > When we discussed the "with type" idea a long time ago (I think it was at > the Intermetrics meeting) the general feeling was that a mechanism for > importing access types was fundamentally important. Type stubs in any of the proposed forms don't give you that capability either, directly. It is an unsolvable problem, given the statement that access types might vary in representation. You then need to resort to the _Pointers package in any of the proposed models (unless we get the pointer compatibility problem solved, too, as part of another AI). As to Tuck's model in the message that starts: > Well I went back and reread both the "with type" Yes. That's exactly the model that I had in mind, too. > disallow renamings of packages whose names are mentioned only > in "with type" clauses. needs some ...smithing, though, since you can't have retroactive illegality of past renamings. It looks solvable, though, by simply not allowing for completion of types taken from renamed packages. ************************************************************* From: Erhard Ploedereder Sent: Friday, December 20, 2002 11:59 AM > I am really annoyed that we are reopening the "with type" proposal, because > there were many technical difficulties associated with it which I suppose we > are going to re-discover. This is turning into a red herring, because I am fairly sure that any problem that might be discovered for the Tucker/my model is equally present in the type stub proposal as written or as amended with the vacuous context clause that merely names the package. Put differently: if we re-discover a problem, we almost certainly have the same problem with the type stub proposal and merely haven't realized it. ************************************************************* From: Robert Dewar Sent: Friday, December 20, 2002 9:28 PM > Hence, I would propose that we go back to the > with-type proposal, with semantic rules taken > in large part from the type-stub proposal. At first glance, I certainly like that approach. ************************************************************* From: Robert Dewar Sent: Friday, December 20, 2002 9:32 PM > My company is not interested in implementing experimental features for the > fun of it, because that's not what our customers want. They want features > that solve their problems, will be supported over the next 10 years, and > will be portable to other compilers. An endorsement by WG9 is (short of the > production of a TC or an Amendment) a good indication that a proposed > feature is likely to be supported by a variety of compilers over long > periods of time. Well I don't share your view that an endorsement of an AI is a guarantee of longevity ... > Wait another year and we will have reached the point where it's impossible > to make a business case for implementing type stubs. This seems a very specific and peculiar situation (peculiar to you), I am not sure that it constitutes a valid argument for rushing. The fact of the matter is that no one can expect to get an immediate ruling on a permanent solution to this problem. I actually think that some experimental implementations would be useful. I know we have several customers using "with type" to solve at least some of these problems. I am not aware of any significant feedback, negative or positive. Perhaps we should try to poll our customer base and get that feedback. P.S. I quite understand the concerns with the model that we implemented. Note that at the time we implemented it, it seems quite likely that it would be adopted and "be supported over the next 10 years etc" :-) ************************************************************* From: Robert Dewar Sent: Friday, December 20, 2002 9:36 PM Note incidentally that my concern about rushing is precisely that I do NOT want another false step here. At a previous point in time, everyone seemed quite happy with the original "with type" proposal, but it was *NOT* rushed "into production", and sure enough problems were found. It is really important this time to get things right. Rushing for WG9 approval, which then has later to be reconsidered because further problems are found does not do any one any good. Pascal, if you really *do* think that you have your hands on a fine proposal, I would encourage you to implement it. In fact arguments based on actual successful usage are very powerful ones. ************************************************************* From: Pascal Leroy Sent: Saturday, December 21, 2002 10:41 AM > > "Tucker would like to abandon the with type proposal. He says it is > > misguided, because you have to posit the existence of "ghost" or > > "incomplete" packages to describe it. In other words, a compiler will need > > to create a hidden package specification, and populate it with the right > > stuff. Then it is possible to see a lot of these ghost packages, which > > provide different views of the same package, and the compiler must figure > > out that they all correspond." > > This problem is significantly simplified, I believe, if you > don't allow the use of package renamings in the name of > the with'ed type. I don't think so. In the case of our technology at least, the problem we had was that the "with type" construct was a very odd beast. In many places we have code that assumes that a type is declared by a bona-fide type declaration, living in a bona-fide declarative part. The "with type" didn't fit that pattern. That's why we came to the conclusion that we had to create these "ghost" packages to go back to a model (type declaration in declarative part) that we were prepared to handle. I realize that this may just reflect a limitation of our technology, but I am always uncomfortable when the compiler has to go through hoops to implement a feature. (We have similar difficulties with children of generics, and they are not the most natural feature of Ada 95 if you ask me.) The more I think of it the less I like the "with type" proposal. To me, the context clauses are for "importing" stuff from other units. Now don't tell me that "with type" imports a type, because that type may not even exist yet (I guess I am echoing Erhard's discomfort at having a with clause that doesn't involve a semantic dependency). The "with type" really declares an incomplete type, but then the normal place where type declarations occur are visible/private parts. So I think it's much more WYSIWYG to place that declaration in the package (like type stubs do) than in the context clauses (like "with type" does). > > "Any compilation unit that has a semantic dependence on another compilation > > unit which has a "with_type_clause," as well as a dependence on the package > > identified by the with_type_clause, must verify that a type declaration (not > > just a subtype declaration) for the named type does in fact appear within > > the visible part of the package." > > I suggested we use the rule from the type-stub proposal *instead* > of this rule. It avoids the problems you had with this one, > I believe. I missed that part of your proposal. Then I agree, it doesn't cause trouble with incremental compilation. ************************************************************* From: Pascal Leroy Sent: Saturday, December 21, 2002 10:50 AM > Pascal, if you really *do* think that you have your hands on a fine proposal, > I would encourage you to implement it. In fact arguments based on actual > successful usage are very powerful ones. Well, I really do think that the type stubs AI as recently sent to WG9 was a fine proposal, and I don't believe that any actual flaw has been found in it. What I hear is a number of people saying that they don't like it for a variety of aesthetical reasons, but I don't think that any technical problem has been found in it. As for implementing it, unfortunately it's not my decision, and I don't see this happening without a WG9 endorsement. ************************************************************* From: Ed Schonberg Sent: Saturday, December 21, 2002 11:27 AM The two technical problems with type stubs: > 3) Problems with type stubs > a) Nothing in context clause to signal presence of stub. > b) Doesn't work as well when all types have the same simple name > (e.g. type Object is ...) seem simpler to solve than the semantic difficulties of "with type". Concerning b), the syntax of an incomplete type declaration could include: type P.T; -- stub name is an expanded name. which is more terse (terser?) that the "separate in" syntax. Subsequent references are of course forced to use the expanded name as well. Concerning a), we could state a rule similar to 10.1.5 (6) for program unit pragmas: "a type stub declaration shall appear before any other nested declarations". This places them as close to the context as possible. This is easy to state and possibly to swallow (even though it it is reminiscent of the Ada83 annoying restrictions on order of declarations). ************************************************************* From: Randy Brukardt Sent: Saturday, December 21, 2002 1:01 PM Ed Schoenberg wrote: > The two technical problems with type stubs: > > > 3) Problems with type stubs > > a) Nothing in context clause to signal presence of stub. > > b) Doesn't work as well when all types have the same simple name > > (e.g. type Object is ...) > > seem simpler to solve than the semantic difficulties of "with type". True. But we made a technical decision not to solve b). Certainly the proposed solution of type Obj is separate in P.T; is simple and straightforward. And it has the advantage of making everything explicit; Ada is about readability, not ease of writing after all. But it was defeated in a straw poll. > Concerning b), the syntax of an incomplete type declaration > could include: > > type P.T; -- stub name is an expanded name. > > which is more terse (terser?) that the "separate in" syntax. Subsequent > references are of course forced to use the expanded name as well. I don't like this for two reasons: * It is not at all clear that this is a stub declaration. We usually favor readability in Ada, not cutting the number of characters. * It requires changing the syntax of type declarations, as they only take identifiers currently. That is the main reason alternative 3 was rejected (explicit completion). * It brings back all of the ugliness of "ghost" packages. In Janus/Ada, the only way for there to be something called P.T is for there to be a program unit called P. But then we have to prevent direct use of P by itself, so a lot of extra code has to be added all over the place. Indeed, we complained about this in child packages during the U/I project, and the standard was eventually changed to eliminate it. (I object to expanded names in any "with type" proposal for the same reasons.) (OK, that's three reasons). > Concerning a), we could state a rule similar to 10.1.5 (6) for program > unit pragmas: "a type stub declaration shall appear before any other > nested declarations". This places them as close to the context as > possible. This is easy to state and possibly to swallow (even though it > it is reminiscent of the Ada83 annoying restrictions on order of > declarations). That would prevent using type stubs in private parts. One of the big advantages of type stubs over the original "with type" is that type stubs can be made to be private. That let's you declare a handle to them without exposing the actual access type to the world: package PP is type Handle is private; ... private type Object is tagged separate in P.T; type Handle is access Object'Class; end PP; I think all of the current proposals (both type stub and with type) would allow private stubs, and I think they are likely to be useful. ************************************************************* From: Randy Brukardt Sent: Saturday, December 21, 2002 1:13 PM Robert Dewar said: > Note incidentally that my concern about rushing is precisely that I do NOT > want another false step here. At a previous point in time, everyone seemed > quite happy with the original "with type" proposal, but it was *NOT* > rushed "into production", and sure enough problems were found. It is > really important this time to get things right. Rushing for WG9 approval, > which then has later to be reconsidered because further problems are > found does not do any one any good. I grudgingly have to admit that Robert's right about rushing. But I also have to agree with Pascal that there aren't any technical (as opposed to aesthetic) problems yet found with the type stub proposal. > Pascal, if you really *do* think that you have your hands on a fine proposal, > I would encourage you to implement it. In fact arguments based on actual > successful usage are very powerful ones. The problem with that is we need a fairly stable solution before trial implementations make sense. If we continue to wildly toss about solutions that aren't remotely similar, then an implementation really doesn't help, because it probably will have little in common with the final solution. That's the problem with GNAT's with type, for instance. The Ada 9x User/Implementor teams didn't spend that much time on proposals that weren't pretty firm. I think more than 80% of our U/I code is still in Janus/Ada. Proposals like finalization and streams which were all over the place until the last minute never got implemented by any of the U/I teams. Not surprisingly, those also are the proposals with the most problems that we've had to fix in the standard. Because of the last factor, and the high importance of this issue, I think it is imperative that we settle on the basic outline of the solution, so that we can get some early implementations to ferret out problems. (In this case, I'm concerned about the complexity of the type matching rules, which would be essentially the same in type stubs or in with type v. 2.) If we continue to be all over the place, we may eventually freeze a solution, but then we'll end up spending a lot of time patching it up in the next five years. So my preference is to stop dreaming up new ideas (because they really don't add much, if anything) and figure out what, if anything, we need to do to make the proposal we have better. ************************************************************* From: Robert Dewar Sent: Saturday, December 21, 2002 1:20 PM > The more I think of it the less I like the "with type" proposal. To me, the > context clauses are for "importing" stuff from other units. Now don't tell > me that "with type" imports a type, because that type may not even exist yet > (I guess I am echoing Erhard's discomfort at having a with clause that > doesn't involve a semantic dependency). The "with type" really declares an > incomplete type, but then the normal place where type declarations occur are > visible/private parts. So I think it's much more WYSIWYG to place that > declaration in the package (like type stubs do) than in the context clauses > (like "with type" does). I think this is too much of an implementation viewpoint. The context clause shows dependencies in the sense of program design, i.e. the meaning of a unit depends on other units at a conceptual level. The "with type" most certainly does say that this unit is dependent on stuff in another unit from a point of view ot semantic understanding. To me it seems quite natural. I really don't mind the exact form, but it seems essential to me that this kind of conceptual "dependence" be represented in the context clause. I want to emphasize here that as far as GNAT and ACT goes, the chocie is neutral, we don't see any particular conceptual difficulties in implementing any of these schemes. To properly complete our existing with type implementation is not likely to be significantly easier than full implementation of any of the schemes under discussion. So my concerns here, unlike Pascal's, are not at all rooted in concerns about difficulty of implementation, but rather on my conceptual feeling that some indication in the context clause is absolutely essential from a methodoloogical point of view. I don't object to taking implementation difficulties into account. There are only in effect a handful of Ada 95 front end technologies at this stage, and it is certainly appropriate to listen if any proposal would cause difficulties, not the least because it would likely be a significant barrier to eventual WG9 approval of a new standard (after all, we made very significant compromises in the initial Ada 95 design, based partly on concerns about difficulties of implementation in technologies that did not even manage to make the Ada 95 transition after all). However, I think that such concerns must take a back seat to fundamental methodological consistency considerations. ************************************************************* From: Robert Dewar Sent: Saturday, December 21, 2002 1:24 PM > Well, I really do think that the type stubs AI as recently sent to WG9 was a > fine proposal, and I don't believe that any actual flaw has been found in > it. What I hear is a number of people saying that they don't like it for a > variety of aesthetical reasons, but I don't think that any technical problem > has been found in it. Well if I am following things correctly, one objection is the lack of an indication in the context clause. For me that is much more than an "aesthetical reason", it is a fundamental flaw, one that would certainly cause me to recommend that the US delegation vote against the proposal. ************************************************************* From: Ed Schonberg Sent: Saturday, December 21, 2002 1:24 PM > > Concerning b), the syntax of an incomplete type declaration > > could include: > > > > type P.T; -- stub name is an expanded name. > > > > which is more terse (terser?) that the "separate in" syntax. Subsequent > > references are of course forced to use the expanded name as well. > > I don't like this for two reasons: > * It is not at all clear that this is a stub declaration. We usually favor > readability in Ada, not cutting the number of characters. > * It requires changing the syntax of type declarations, as they only take > identifiers currently. That is the main reason alternative 3 was rejected > (explicit completion). It is a stub declaration precisely because the syntax is not that of a type declaration. From a parsing point of view it's trivial, and if it is placed next to the context clause it's even easier to recognize. > * It brings back all of the ugliness of "ghost" packages. In Janus/Ada, the > only way for there to be something called P.T is for there to be a program > unit called P. But then we have to prevent direct use of P by itself, so a > lot of extra code has to be added all over the place. Indeed, we complained > about this in child packages during the U/I project, and the standard was > eventually changed to eliminate it. (I object to expanded names in any "with > type" proposal for the same reasons.) This is semantically no different that the current stubs proposal. Why are those "ghost" packages not a problem with stubs? > > Concerning a), we could state a rule similar to 10.1.5 (6) for program > > unit pragmas: "a type stub declaration shall appear before any other > > nested declarations". This places them as close to the context as > > possible. This is easy to state and possibly to swallow (even though it > > it is reminiscent of the Ada83 annoying restrictions on order of > > declarations). > That would prevent using type stubs in private parts. One of the big > advantages of type stubs over the original "with type" is that type stubs > can be made to be private. That let's you declare a handle to them without > exposing the actual access type to the world. Well, the desire to give some context-level indication that these are imported types is in serious conflict with the desire to hide them! Given that in the majority of cases these types are introduced in order to define access types for them, placing the stub in the private part seems to me like a rather transparent fig leaf. Of the various conflicting requirements here, having private stubs seems to me the weakest. ************************************************************* From: Robert Dewar Sent: Saturday, December 21, 2002 1:28 PM > Concerning a), we could state a rule similar to 10.1.5 (6) for program > unit pragmas: "a type stub declaration shall appear before any other > nested declarations". This places them as close to the context as > possible. This is easy to state and possibly to swallow (even though it > it is reminiscent of the Ada83 annoying restrictions on order of > declarations). I really prefer a separate indication in the context clause rather than this restriction of placement. And I don't see what the big deal is in introducing this separate indication. ************************************************************* From: Randy Brukardt Sent: Saturday, December 21, 2002 2:40 PM > > > Concerning b), the syntax of an incomplete type declaration > > > could include: > > > > > > type P.T; -- stub name is an expanded name. > > > > > > which is more terse (terser?) that the "separate in" syntax. Subsequent > > > references are of course forced to use the expanded name as well. > > > > I don't like this for two reasons: > > * It is not at all clear that this is a stub declaration. We usually favor > > readability in Ada, not cutting the number of characters. > > * It requires changing the syntax of type declarations, as they only take > > identifiers currently. That is the main reason alternative 3 was rejected > > (explicit completion). > > It is a stub declaration precisely because the syntax is not that of a > type declaration. From a parsing point of view it's trivial, and if it is > placed next to the context clause it's even easier to recognize. Huh? It declares a type, so "of course" its a type declaration. The only way to introduce a type currently is via a type declaration; it seems very strange to add a new concept that is almost the same. > > * It brings back all of the ugliness of "ghost" packages. In Janus/Ada, the > > only way for there to be something called P.T is for there to be a program > > unit called P. But then we have to prevent direct use of P by itself, so a > > lot of extra code has to be added all over the place. Indeed, we complained > > about this in child packages during the U/I project, and the standard was > > eventually changed to eliminate it. (I object to expanded names in any "with > > type" proposal for the same reasons.) > > This is semantically no different that the current stubs proposal. Why are > those "ghost" packages not a problem with stubs? Yes it is, because there is no "expanded" name in the current proposal. (the thing following the "in" is not a name at all -- that is very important, bother for dependency reasons and for implementation reasons). "Ghost" packages come up when you have a name "P.T" without a name "P". ************************************************************* From: Pascal Leroy Sent: Sunday, December 22, 2002 4:36 AM > That let's you declare a handle to them without > exposing the actual access type to the world: > > package PP is > type Handle is private; > > ... > private > type Object is tagged separate in P.T; > type Handle is access Object'Class; > end PP; > > I think all of the current proposals (both type stub and with type) would > allow private stubs, and I think they are likely to be useful. That's an intriguing example when you modify it to include a nested package: package QQ is package PP is type Handle is private; private type Object is tagged separate in P.T; type Handle is access Object'Class; end PP; end QQ; With type stubs you can precisely control which regions have visibility over the incomplete type. With "with type" the entire unit (with the possible exception of the visible part) has visibility over it. I am not claiming that the former is a superduper important capability, but it certainly seems nicer to me to have fine visibility control. ************************************************************* From: Pascal Leroy Sent: Monday, December 23, 2002 4:39 AM Well if I am following things correctly, one objection is the lack of an > indication in the context clause. For me that is much more than an > "aesthetical reason", it is a fundamental flaw, one that would certainly > cause me to recommend that the US delegation vote against the proposal. Just to clarify, I am happy with the notion of adding some kind of indication in the context clause of which units are forward-referenced. Of all the proposals that I have seen so far, the one I prefer is Tuck's "[private] [separate] with P;". What I object to is the notion of resurrecting "with type" from the dead. ************************************************************* From: Randy Brukardt Sent: Friday, January 17, 2003 8:31 PM Here's the list of problems that was posted during the previous discussions, renumbered so that they have the same numbers as the alternatives. 1) Problems with with-type a) Want to share access type, but no way to import various rep clauses b) Context clause visibility is not transitive, so this complicates type compatibility rules c) Renaming created type compatibility snafus 2) Problems with package abstracts a) New kind of compilation unit could be major disruption to tools b) Not clear what kinds of declarations may appear in package abstract c) Must everything be repeated in package spec, or is package abstract just a prefix on the package spec? d) Not obvious in package spec whether package abstract does or does not exist (bad memories of optional package bodies and optional subprogram specs) 3) Problems with type stubs with explicit completion a) there can be multiple stubs for the same full declaration (should be supported; but even if declared illegal, seems rather difficult to check) b) there may be erroneously multiple full declarations for a single stub (must be illegal, but not easy to check) c) would presumably run afoul the same critique, in even worse ways, as the "type stub identifies package" model. 4) Problems with type stubs with completing package name a) Nothing in context clause to signal presence of stub. b) Doesn't work as well when all types have the same simple name (e.g. type Object is ...) All) All proposals share: a) Incompatible access types formed over multiple views of the type. -- We don't need to discuss #2 and #3 further, I think. (Well, I don't really want to discuss any of them again, but since a number of people insisted. I'm going through this exercise to help motivate alternative #5, which I shall present shortly). All (a) is misleading. In the intended use of type stubs, you declare a single access type in the interface package, and use that everywhere, including in the spec. of the base package. As in the example in AI-217-04: package Employees_Interface is type Employee is tagged separate in Employees; type Emp_Ptr is access all Employee'Class; end Employees_Interface; with Employees_Interface, Department_Interface; package Employees is type Employee is tagged private; subtype Emp_Ptr is Employees_Interface.Emp_Ptr; procedure Assign_Employee(E : in out Employee; D : in out Departments_Interface.Department); ... function Current_Department(D : in Employee) return Departments_Interface.Dept_Ptr; end Employees; Note that the subtype Emp_Ptr re-exports the access type, so that everyone can use the same type. So problem All (a) is a problem if-and-only-if your program isn't structured very well. I don't think we should concern ourselves with that. Tucker's (virtual) proposal for with types rewrites purports to fix problems 1(a) and 1(c). Clearly problem 1(b) remains. With the rules has Tucker proposed them, it is impossible to re-export a with type. That means that every package that needs to use the type will have to import it, even if it imports everything else from an interfaces package. That seems fundamental to the proposal. Tucker's problem list did not include 1(d): Visibility problems and/or ghost packages required. That is a critical omission, because the solution to it complicates any with type proposal a lot. The problem is basically that if you have a name P.T, then you have to do something about when P is used alone. If P.T is a name, but P is not, then you have greatly complicated the handling of program unit expanded names, since such a prefix cannot be ambiguous on its own. In an example like: package Parent is function P return Natural; end Parent; with type P.T; package Parent.Child is type Ptr_T is access all P.T; C : constant Natural := P; -- Function P? end Parent.Child; The visibility of P.T and the parent P get mixed up. Similarly, if someone writes the rather nonsense: with P; with type P.T; you have to figure out what happens. Solving this seems to require the introduction of ghost packages. For me, there is also a 1(e): "with type" is inconsistent with the other uses of the context clause, and looks like a kludge. Why just "with type"? Why not "with exception" or "with constant"?? Please don't bring up "use type": it is precisely an example of what not to do here. The same question can be asked of it, why not "use constant" or "use exception"? Moreover, it isn't even "use type"; it's "use operators of type". Related to that, the items in a with_clause are all program units. Why are we suddenly adding types? Tuck has previously complained that he doesn't like the new features to look like add-ons. This one certainly does. ************************************************************* From: Randy Brukardt Sent: Friday, January 17, 2003 8:32 PM The objections to the with the half-worked out "with separate" clause were primarily: (1) A with_clause that doesn't make a semantic dependence looks inconsistent; (2) Some odd objection against re-exporting these types. I completely fail to understand (2). I view that as a major, unfixable flaw of any with type like proposal. Moreover, type stubs allow much better control over visibility than with type. Ada has often been criticized for not having fine visibility control on compilation units; I don't see the appeal of adding more features with that problem. OTOH, I've come around on the users concerns. When we see a with clause, we see "semantic dependence". But that's language lawyer talk. Users just see dependence. It's easy to think of a with_clause as a "strong dependence", and our new clause (whatever it may be) to be a "weak dependence". I'm pretty sure that is how people will think of it. Still, I started thinking about both the solutions to problems 4(a) and 4(b), and 5(1). I wondered if Dan Eilers wasn't on the right track when he proposed a specially named package to hold these things. After all, problem 3(b) (the multiple names problem) isn't a real problem if you have an interface package for every type in the system. (And that seems like a good idea, as it eliminates the multiple access type problem, by providing a single point of declaration for it.) Dan's proposal: package P'Interface is has some advantages, but the significant disadvantage of a new kind of name. I'd probably make 'Interface an attribute, rather allow it to be anything, but that doesn't help much. So I turned to other ways to look at this. Perhaps a package modifier would do the trick: package P_Interface is interface of P is ... This worried me because we now have two new proposals, both called interfaces. (One is interfaces types, and the other is interfaces packages, but that seems much too easy to confuse.) So I want back to the package abstract proposal and stole some terminology: package P_Abstract is abstract of P is ... Well, the two "is"s don't look great, and besides, you find out about this too late. It probably should go in front: abstract of P; package P_Abstract is... And I've arrived back at a new context clause item. However, this one eliminates Erhard's objection that we'd be adding a with_clause that doesn't make a semantic dependence. It also doesn't have the naming problems of "with type" (any version). It also reinforces the intended use of the feature (and of type stubs). It also doesn't look quite as much like a kludge, because it is easy to imagine other types of stubs in the future (constant stubs, exception stubs, and especially a new kind of subprogram stub). So this doesn't lock us in like "with type" does. The model is that this represents an "abstracted package" from which we can use selected details without making a semantic dependence (a "strong dependence" as noted above). It would be easy to then call a package having one of these clauses a "package abstract", but clearly it can contain more than just the contents of the abstracted package. Anyway, I've written this up completely (including all of the wording), so we can see how it plays out. That will be alternative #5, which I'll send in the next message [This is AI-217-05/01, ED]. Obviously, we can tweak the terminology or even go back to "with separate" without actually changing much of the wording, so I hope that this is not wasted effort. ************************************************************* From: Robert I. Eachus Sent: Friday, January 17, 2003 11:18 PM Part of the reason I haven't been posting for a while was trying to get my head around this whole issue. I like the current alternative. I can also understand why some people have trouble with the package containing the type not being mentioned in the context clause. So far I have come to two firm conclusions on how to fix this: 1) The name of the package may be required in some context clause, but putting the type name there leads to trouble. 2) There needs to be an exclusion rule. Whatever gets added to the context clause, it must be illegal to have a with clause for the same package in the context clause. Point 2 actually starts to make a great deal of sense out of user's objections. The context clause is the proper place for context requirements, and distributing them through the entire specification is a step in the wrong direction. Now let me have some Norm Cohen style fun. use type in Foo; ... type Bar is separate in Foo; ... By putting a "use", not a "with" in the context clause, there is no need to wrap our heads around the axle to make sure we don't need to change text elsewhere, or to define which withs are really withs. (It might be nice to do away with the current grammar restriction that requires a context clause to begin with "with", or pragmas followed by "with", but that is a detail.) Oh, one final note. I see no problem with: with A; use type in A.B; I think that the methodological rule should be followed that to use a type from a child package, the parent has to be named earlier in a with in the context clause. This might end up in some cases requiring something like: with A; use type in A.B; package A.C is... I can accept that. However, notice that: with A; use type in A.B; package A is.... should and would be illegal. ************************************************************* From: Randy Brukardt Sent: Wednesday, January 22, 2003 7:43 PM I want to give a more realistic example than in the AIs, so that we can see how it will work with the various solutions proposed. So, I'm going to describe how I would use alternative #5 (and #4, for that matter) in the Claw Builder. This example shows (I think) why I think that non-transitivity is a non-starter. The Claw Builder is a real, existing program which could use this facility. The problem is that some objects need references to other types of objects, and these needs are circular. For instance, (some) types of window objects include menu objects. And some types of menu objects include actions that open a window. The current Claw Builder solves this problem by using names rather than access objects to connect the objects in some cases. This is usually done only where necessary to break circularities. For instance, menu objects name the windows they are to open, rather than linking to them. Using names causes several problems: -- Accesses to the linked object is much slower, as they have to be looked up by name before use; -- If the user renames the linked object, we have to walk the entire project to insure any names are updated; -- If the user copies the linked object and then renames the copy (the required behavior), we have to be careful NOT to walk the project and update names -- harming code reuse. -- We can't have overloaded names (not a problem for windows, but can happen in other cases). A root window object is an abstract object with a fairly large set of operations. Each concrete object has to provide implementations for many of these operations (some it can inherit). All of these operations are dispatching. Typically, a user of the operation would apply it to a list of windows using an iterator generic, with the operation dispatching to the correct implementation. For the purposes of this discussion, we'll look at just a few: Show, Hide, Display_Name. The existing package looks something like: package CBuild_Root is type Root_Window_Type is abstract new Ada.Finalization.Limited_Controlled with private; type Any_Window_Access_Type is access all Root_Window_Type'Class; procedure Show (Window : in out Root_Window_Type) is abstract; procedure Hide (Window : in out Root_Window_Type) is abstract; function Display_Name (Window : in Root_Window_Type) return String is abstract; ... end CBuild_Root; Now, I would construct an abstract of this package for use in circular definitions. (Caveat, I haven't actually done this, not having a compiler with this feature.) Using the syntax of alternative #5, this would look like: abstract of CBuild_Root_Definition; package CBuild_Root is type Root_Window_Type is tagged separate in CBuild_Root_Definition; type Any_Window_Access_Type is access all Root_Window_Type'Class; procedure Show (Window : in out Root_Window_Type'Class); procedure Hide (Window : in out Root_Window_Type'Class); function Display_Name (Window : in Root_Window_Type'Class) return String; ... end CBuild_Root; This abstract actually can be used anywhere that you need a Root_Window_Type, except when you need to construct an object of Root_Window_Type, and when you need to create a new concrete object. (It turns out that these are the same places in the Claw Builder, but that may not be true generally.) All other uses would use the abstract and not the "real" package. That's why I named it with the name of the current "root" package. In order to keep the access types compatible, the real package would be modified to: with CBuild_Root; package CBuild_Root_Definition is type Root_Window_Type is abstract new Ada.Finalization.Limited_Controlled with private; subtype Any_Window_Access_Type is CBuild_Root.Any_Window_Access_Type; procedure Show (Window : in out Root_Window_Type) is abstract; procedure Hide (Window : in out Root_Window_Type) is abstract; function Display_Name (Window : in Root_Window_Type) return String is abstract; ... end CBuild_Root_Definition; The body of CBuild_Root would need visibility on the real type so that it could implement the procedures: with CBuild_Root_Definition; package body CBuild_Root is procedure Show (Window : in out Root_Window_Type'Class) is begin CBuild_Root_Definition.Show (Window); end Show; procedure Hide (Window : in out Root_Window_Type'Class) is begin CBuild_Root_Definition.Hide (Window); end Hide; function Display_Name (Window : in Root_Window_Type'Class) return String is begin return CBuild_Root_Definition.Display_Name (Window); end Display_Name; ... end CBuild_Root; Now, essentially any use of an Any_Window_Access_Type pointer could be implemented without withing the "abstracted package" at all. Only the packages define the concrete window objects would with CBuild_Root_Definition at all. For instance, in the menu case, we would have something like (this is greatly simplified): with CBuild_Root; package CBuild_Menu is type Menu_Item is record Name : String (1..20); Action : Action_Type; Dialog : CBuild_Root.Any_Window_Access_Type; -- If Action=Open_Dialog. end record; procedure Simulate_Action (Item : in Menu_Item); end CBuild_Menu; with CBuild_Root; package body CBuild_Menu is procedure Simulate_Action (Item : in Menu_Item) is begin if Item.Action = No_Action then null; elsif Item.Action = Open_Dialog then CBuild_Root.Show (Item.Dialog.all); ... -- Other actions. end if; end Simulate_Action; end CBuild_Menu; The dereference in Show is allowed, because (in implementation terms) we don't need to know anything at all about the real type in order to generate the call. Such a call cannot be dispatching (the only way to see a primitive operation is for the completion to be "available"). Thus, it cannot be using a tag. And we know the parameter passing mode (by reference), and we know that the body (or at least some body) must be in the scope of the completion, or nothing at all must be done with the type. I believe Tucker's rules in Alternatives #4 and #5 do allow this dereference. ************************************************************* From: Tucker Taft Sent: Wednesday, January 22, 2003 7:54 AM Here is yet another approach to the circular dependence problem. It has the following nice characteristics: 1) No new kinds of compilation units 2) No new kinds of context clauses 3) No new kinds of type definitions 4) Minimal syntax inventiveness It was inspired by trying to deal with Dan's ideas, Randy's ideas, and the original package "abstract" idea. Now that I have you all breathless with anticipation... package P is ... child package C is type T; type T_Ptr is access T; end C; ... end P; The basic idea is that you can give a little "taste" (or "prefix") of a child package in the parent package spec. Anyone who "with"s the parent sees the "prefix" of the child. If they "with" the child explicitly, then they see the whole spec of the child package. This is analagous to the notion of a package subunit, except that it is in the spec rather than the body, and you reveal part of the spec of the child. Any sort of declaration may appear in such a child package prefix. The full visible part of the child package is simply the concatenation of the prefix and the part that is compiled separately. Since we know that any unit that "with"s a child implicitly "with"s its parent, there will never be a situation where a unit will see the "full" child but not see the prefix. Pretty cool, eh? Fire away... ************************************************************* From: Robert Dewar Sent: Wednesday, January 22, 2003 8:16 AM I like this at first glance, but one query, why not just say package P.C is instead of introducing the keyword child. The reason I don't like this particular choice of keywords is that it is odd to have child units as a first class feature of the language, and child as a keyword, and yet (in the normal case) child units do not use the keyword child. (Also child would be a pretty deadly keyword to add, since it is a VERY common identifier in any program dealing with trees etc). ************************************************************* From: Robert A. Duff Sent: Wednesday, January 22, 2003 10:09 AM Tuck says: > > Pretty cool, eh? Yes. Robert says: > I like this at first glance, but one query, why not just say > > package P.C is > > instead of introducing the keyword child. I think I like Robert's suggested syntax. ************************************************************* From: Jean-Pierre Rosen Sent: Wednesday, January 22, 2003 10:39 AM If I understand correctly, this is the old package abstract stuff, with the additional constraint that the abstract must be for a child, and declared within the parent. Why not then: package abstract P.C is .... ************************************************************* From: Robert I. Eachus Sent: Wednesday, January 22, 2003 1:22 PM The idea looks good, but it will need a lot of work. The key to the proposal is that incomplete types can appear in the abstract (or whatever it is called) of the child, and completion deferred until the spec of the child is compiled. As for any other "goodies," I am strongly in favor of a minimalist approach. I think that allowing access and discrete types as well is harmless enough and methodologically sound, but beyond that I hesitate to go. (The access types could be declared in the parent, but I don't like that on methodological grounds, and enumeration types and other discrete types may be needed for discriminants.) Incidentally if complete types are permitted, they should be required to be repeated in the actual child package spec, but the end of the abstract should be a freezing point. I can probably be convinced that there is a need for constants--I am not convinced, but I can be. However, subprograms, task types, protected types, and variables all seem to open unnecessary cans of worms. Oh, and I will offer one other syntax proposal: separate package B is {...} end B; We could even allow: separate package A; As a way of specifying that a child of that name must be part of the partition, but nothing else. ************************************************************* From: Tucker Taft Sent: Wednesday, January 22, 2003 2:45 PM Yes, this proposal is very similar to the "package abstract" or "package P'Interface" ideas. I chose the "child" keyword just to avoid any tricky keyword convolutions or reinterpretations. I don't care too much what we use. As JP says, the important thing is to put the interface/prefix/abstract/"taste" into the parent spec. This seems to solve a whole bunch of problems. Presumably a private child's prefix would have to go into the private part of the parent spec. As far as syntax, we might have: - child package C is ... end C; - package P.C is ... end P.C; - package abstract P.C is ... end P.C; - package abstract C is ... end C;(for completness) - separate package C is ... end C; - package C is separate ... end C; I find it odd to declare something *textually* inside P with the name "P.C". There is no precedent for that. So I would prefer the ones that just talk about "C". The ones with the word "separate" seem nicely suggestive of body stubs, which are certainly pretty close to this. Note that body stubs don't use the "P.C" style name for the stub. Although we have talked about "package abstracts" this really isn't an "abstract" in the sense of a summary. It is more of a "prefix" in that I am presuming declarations are *not* repeated in the "full" visible part (there seems no reason to repeat them, and dealing with conformance of a sequence of declarations makes my hair stand on end ;-). Putting the word "separate" after the "is" is more consistent with stubs, and the only change is that rather than an immediate ";" you may follow it with one or more declarations and an "end [id];". And of course, the keyword "body" is omitted. I don't think I agree with Robert Eachus' desire to restrict the kind of declarations that could go in the prefix, but I do agree that anything that is not an incomplete type declaration will be frozen at the end of the enclosing library unit spec, in the same way things in a nested package spec are frozen when reaching the end of the enclosing library unit spec. So no giving some but not all of the dispatching operations, or no deferring the rep-clause for a (non-incomplete) type to the full visible part. ************************************************************* From: Dan Eilers Sent: Wednesday, January 22, 2003 7:43 PM > Yes, this proposal is very similar to the "package abstract" > or "package P'Interface" ideas. I believe Randy and my recent proposals have the full type in the parent and the type stub in the child, which is backwards from this idea. It seems more intuitive to me to have the full type in the parent, since that is what would normally be with'd, but maybe its not a big deal. > Presumably a private child's prefix would have to go > into the private part of the parent spec. If you have a such a package prefix in a private part, do you propose that the corresponding child package could contain the completion of a declaration in the visible part? If so, that might solve the need for separately compiled private parts. ************************************************************* From: Randy Brukardt Sent: Wednesday, January 22, 2003 9:04 PM On first glance this proposal doesn't look too bad, but I have some concerns. > Here is yet another approach to the circular dependence > problem. It has the following nice characteristics: > > 1) No new kinds of compilation units > 2) No new kinds of context clauses > 3) No new kinds of type definitions > 4) Minimal syntax inventiveness This reminded me of Wirth's Turing Award lecture. In it, (talking about PL/1), he said the initial descriptions of features were "promises of unalloyed delight", but that as the work went on, the descriptions got messier and messier... (I hope I haven't misquoted Prof. Wirth.) Anyway, one minor nit is that (3) isn't true. Every proposal we've worked on leans heavily on tagged incomplete types. Indeed, I can come close to solve these problems with those alone (and not other feature at all). Without them, you can't do enough with the incomplete types to make any of these ideas worthwhile. (And we sure as heck don't want to allow 'Class on things that might not turn out to be tagged.) And of course (1) is a fudge here. You are adding a new kind of program unit, and you've just chose to hide it inside a "normal" compilation unit. Which leads to one concern: usually these things are used at the root of type trees, because it is usually the intersection of different object classes where circularity problems come up. This idea means that you have to introduce a dummy parent package just to hold this child. For instance, for my CBuild example: package CBuild_Windows is package Root is separate -- I really want to type an 'is' here... type Root_Window_Type is tagged; type Any_Window_Access_Type is access all Root_Window_Type'Class; procedure Show (Window : in out Root_Window_Type'Class); procedure Hide (Window : in out Root_Window_Type'Class); function Display_Name (Window : in Root_Window_Type'Class) return String; end Root; end CBuild_Windows; More annoying is that you can't even rename this away at the library level (as the rename would clearly refer to the full package, not just this stub). You'd have to rename in every package that it is used in. Since I would plan to use this in as many places as possible, that is annoying. This also seems to be a pit of visibility issues. We would have a new concept of this additional "part" messing up visibility. Clearly, we'd have to check for and disallow homographs in the real spec. But we'd also have to change the names of the "real" operations, because otherwise all of the calls would be ambiguous. package CBuild_Windows.Root is type Root_Window_Type is limited tagged with private; -- Completion procedure Real_Show (Window : in out Root_Window_Type); procedure Real_Hide (Window : in out Root_Window_Type); function Real_Display_Name (Window : in Root_Window_Type) return String; end CBuild_Windows.Root; We can't call the real operation "Show" here, because that would make it impossible to call Show anywhere is this 'with'ed. (Well, at least without a full qualification on the argument, which leaves a lot to be desired). At least with Alternatives #4 & #5, you can use the package name to separate them, or "use" just one of the packages. Indeed, it would be rare that you'd with both anyway. I presume that you are restricting these "package spec. stubs" (for the lack of a better name) to library level package specifications? The rules for nested ones make my head hurt. I wonder what the rules for use of the incomplete type ought to be? If you can't write (as in my example): with CBuild_Window; package CBuild_Menu is type Menu_Item is record Name : String (1..20); Action : Action_Type; Dialog : CBuild_Window.Root.Any_Window_Access_Type; -- If Action=Open_Dialog. end record; procedure Simulate_Action (Item : in Menu_Item); end CBuild_Menu; with CBuild_Window; package body CBuild_Menu is procedure Simulate_Action (Item : in Menu_Item) is begin if Item.Action = No_Action then null; elsif Item.Action = Open_Dialog then CBuild_Window.Root.Show (Item.Dialog.all); ... -- Other actions. end if; end Simulate_Action; end CBuild_Menu; then we've lost quite a bit of expressive power that we have with Alternatives #4 & 5. OTOH, there might be an opportunity to simplify the existing rules some. So I'd like to know what they'd be. Elaboration is a disaster, but that's true for any solution to this problem. (You really have little choice but to elaborate all of the specs first, then all of the bodies.) Still, it's the visibility rules that worry me the most. I fear that it will take two years to figure them out; we won't have a clue about them two weeks from now at Padua. I know I can't do it in time for the meeting, I have a lot of other AIs that need to be done. The notion of having different visibility to different parts of the spec. is scary, and this seems like a big, big job in the symbol table of a compiler (even if it isn't a big job conceptually). So we need a proposal with the visibility details worked out before I could even imagine to decide if it really is simpler, rather than just appearing that way without the details worked out. ************************************************************* From: Tucker Taft Sent: Wednesday, January 22, 2003 10:03 PM > Anyway, one minor nit is that (3) isn't true. Every proposal we've worked on > leans heavily on tagged incomplete types. Indeed, I can come close to solve > these problems with those alone (and not other feature at all). Without > them, you can't do enough with the incomplete types to make any of these > ideas worthwhile. (And we sure as heck don't want to allow 'Class on things > that might not turn out to be tagged.) True, we should add the tagged incomplete types. > And of course (1) is a fudge here. You are adding a new kind of program > unit, and you've just chose to hide it inside a "normal" compilation unit. Well we were told that having a new kind of compilation unit was a much bigger deal, since the program library support is scattered across more tools than just the compiler. Similarly, new kinds of context clauses and new kinds of interdependencies between units also seems to cause changes in non-compiler tools. So an important feature of this proposal is that the changes are only *within* compilation units. > Which leads to one concern: usually these things are used at the root of > type trees, because it is usually the intersection of different object > classes where circularity problems come up. This idea means that you have to > introduce a dummy parent package just to hold this child. I don't feel too worried about this, since a lot of subsystems seem to have a root package that is almost empty (e.g. Ada.Numerics, and this forms a natural place to host these prefix packages. > ... For instance, for > my CBuild example: > > package CBuild_Windows is Why wouldn't this be package (subsystem) named "Cbuild" and then child named Windows? > package Root is separate -- I really want to type an 'is' here... > type Root_Window_Type is tagged; > type Any_Window_Access_Type is access all Root_Window_Type'Class; > procedure Show (Window : in out Root_Window_Type'Class); > procedure Hide (Window : in out Root_Window_Type'Class); > function Display_Name (Window : in Root_Window_Type'Class) > return String; > end Root; > end CBuild_Windows; > > More annoying is that you can't even rename this away at the library level > (as the rename would clearly refer to the full package, not just this stub). > You'd have to rename in every package that it is used in. Since I would plan > to use this in as many places as possible, that is annoying. I've lost you on the rename issue. Just because a child package has a prefix, there should be no reason you couldn't rename it. > This also seems to be a pit of visibility issues. We would have a new > concept of this additional "part" messing up visibility. Clearly, we'd have > to check for and disallow homographs in the real spec. But we'd also have to > change the names of the "real" operations, because otherwise all of the > calls would be ambiguous. Again, I have lost you. Why are you putting any operations in the package prefix? And what makes these operations more or less real than operations in the full package? I don't see any visibility issues, so we are clearly thinking about this differently. There seems no particular reason to put anything in a package prefix other than a few type declarations. Operations seem redundant (though I don't see any technical reason to disallow them). > package CBuild_Windows.Root is > type Root_Window_Type is limited tagged with private; -- Completion > procedure Real_Show (Window : in out Root_Window_Type); > procedure Real_Hide (Window : in out Root_Window_Type); > function Real_Display_Name (Window : in Root_Window_Type) > return String; > end CBuild_Windows.Root; > > We can't call the real operation "Show" here, because that would make it > impossible to call Show anywhere is this 'with'ed. Again, I have lost you why you declared various class-wide operations in the package prefix. The "with" type and the type stub didn't allow any operations to be declared, so I don't understand why you need them with this proposal. > (Well, at least without a > full qualification on the argument, which leaves a lot to be desired). At > least with Alternatives #4 & #5, you can use the package name to separate > them, or "use" just one of the packages. Indeed, it would be rare that you'd > with both anyway. I have completely lost you now. > I presume that you are restricting these "package spec. stubs" (for the lack > of a better name) to library level package specifications? The rules for > nested ones make my head hurt. I don't see any need for nested prefixes, though I don't see any particular difficulty here. My model is that these would be handled pretty much the same way specs and bodies are handled. That is, when you get to the full package spec, you reenter the scope you were in when you processed the prefix, and keep adding more declarations. From the "outside" if you only "with" the parent, then you only see the declarations in the prefix. If you "with" the child, then you see the declarations in both the prefix and the full spec for the child. I don't understand where the visibility complexity comes in. There are a number of places already in Ada where different places see more or fewer declarations within a package. E.g., when in a private child, you see both the visible and private part. When in a stub, you see all declarations that precede the stub. Can you give me a simple example of the visibility issues you perceive? I am getting mixed up by your relatively complicated example, and your apparent desire to declare class-wide operations in the package prefix. > I wonder what the rules for use of the incomplete type ought to be? If you > can't write (as in my example): > > with CBuild_Window; > package CBuild_Menu is > type Menu_Item is record > Name : String (1..20); > Action : Action_Type; > Dialog : CBuild_Window.Root.Any_Window_Access_Type; > -- If Action=Open_Dialog. > end record; > procedure Simulate_Action (Item : in Menu_Item); > end CBuild_Menu; > > with CBuild_Window; > package body CBuild_Menu is > procedure Simulate_Action (Item : in Menu_Item) is > begin > if Item.Action = No_Action then > null; > elsif Item.Action = Open_Dialog then > CBuild_Window.Root.Show (Item.Dialog.all); > ... -- Other actions. > end if; > end Simulate_Action; > end CBuild_Menu; > > then we've lost quite a bit of expressive power that we have with > Alternatives #4 & 5. OTOH, there might be an opportunity to simplify the > existing rules some. So I'd like to know what they'd be. I need more help understanding the power you are talking about. Can you be more explicit? I am lost in the details of this example. > Elaboration is a disaster, but that's true for any solution to this problem. > (You really have little choice but to elaborate all of the specs first, then > all of the bodies.) I don't understand why elaboration is affected at all. whether a declaration is in a nested package or in the enclosing package is irrelevant for elaboration. The elaboration happens in the order of the declarations within a single package-ish compilation unit. I don't see this changing even if a package prefix happens to be nested inside the compilation unit. Clearly the parent spec compilation unit has to be elaborated before any child compilation units, but the child compilation units don't need to have all their specs elaborated before any bodies. One of the main advantages of this proposal as I see it is there would be *no* change to compilation-unit-level processing, such as compilation unit elaboration order determination. > Still, it's the visibility rules that worry me the most. I claim they are intuitively obvious! Obviously we must not have the same understanding of how this proposal would work. > I fear that it will > take two years to figure them out; we won't have a clue about them two weeks > from now at Padua. I know I can't do it in time for the meeting, I have a > lot of other AIs that need to be done. The notion of having different > visibility to different parts of the spec. is scary, and this seems like a > big, big job in the symbol table of a compiler (even if it isn't a big job > conceptually). Well I don't know anything about your compiler, of course, but in ours, each declarative region has a sequence of declarations, each declaration having its own "logical position." All lookups within a region need to take a logical position "cutoff" which is the last declaration that is visible from where the lookup is taking place. This deals with private part visibility, visibility at the point of a stub, etc. Clearly if you only "with" the parent spec, then the last declaration visible in the child package region is the end of its prefix. > So we need a proposal with the visibility details worked out before I could > even imagine to decide if it really is simpler, rather than just appearing > that way without the details worked out. I will be happy to do that, but I would appreciate some better understanding of the problems you perceive, because I fear I will not address these problems otherwise. ************************************************************* From: Tucker Taft Sent: Wednesday, January 22, 2003 10:10 PM Dan Eilers wrote: >>Yes, this proposal is very similar to the "package abstract" >>or "package P'Interface" ideas. > > I believe Randy and my recent proposals have the full type in > the parent and the type stub in the child, which is backwards > from this idea. It seems more intuitive to me to have the full > type in the parent, since that is what would normally be with'd, > but maybe its not a big deal. I seems important that some compilation units only see the incomplete type, while others see both the incomplete type and its completion. The existing rules for child packages is that if you see a child unit, then you always see its parent. Changing that seems pretty disruptive. Presuming you don't change that, then clearly the incomplete type needs to be in the parent unit, while the completion needs to be in the child unit. >>Presumably a private child's prefix would have to go >>into the private part of the parent spec. > > If you have a such a package prefix in a private part, do you > propose that the corresponding child package could contain the > completion of a declaration in the visible part? If so, that > might solve the need for separately compiled private parts. My hope is that the rules would be changed as little as possible, meaning that if an incomplete type is declared in the prefix of the visible part of the child, the it has to be completed somewhere later in the visible part, either in the prefix itself, or in the visible part of the "full" spec. This wouldn't depend on whether the child was private or public. What I was impllying was that if the child is public, then its prefix (if any) must be in the visible part of its parent, and if a child is private, then its prefix (if any) must be in the private part of its parent. I do agree that this proposal might reduce the need for separately compiled private parts, though I haven't really pursued that investigation. I am mostly trying to come up with a simple solution to the circularly dependent package problem ************************************************************* From: Pascal Leroy Sent: Thursday, January 23, 2003 3:48 AM > package P is > ... > child package C is > type T; > type T_Ptr is access T; > end C; > ... > end P; I hate the notion that the type structure is tied to the package structure, as it makes changes extremely painful during maintenance. Assume that you have two root library packages P1 and P2 declaring types T1 and T2. Assume moreover that T1 depends on T2. For instance: with P2; package P1 is type T1 is record C : P2.T2; end record; end P1; package P2 is type T2 is ... ; end; At this point, in a real project, you probably have a lot of code that references names in P1, including P1.T1. Say that, during maintenance, you have to introduce a circularity, and T2 must now contain a pointer to T1. You would presumably do this by adding a "prefix" and pushing down the full type declaration for T1 in a child unit: package P1_Root is child package P1 is type T1; type AT1 is access T1; end P1; end P1_Root; with P2; package P1_Root.P1 is type T1 is record C : P2.T2; end record; end P1_Root.P1; with P1_Root; package P2 is type T2 is record C : P1_Root.P1.T1; end record; end P2; Now this means that most of the references to P1 must change to reference P1_Root.P1. This includes the places that declare objects of type T1 or otherwise need to view the full type declaration for T1, as well as places that need to reference operations on T1. True, you can alleviate this somewhat by providing library-level renamings or skins, but that's still a lot of work for a rather mundane change. When I do that in C++ all I have to do is type the 5 letters c, l, a, s, s. When I do that in Ada I would have to stand on my head? Come on! Note that none of the other proposals have that drawback, as they don't require that you change the place where the full type (and its operations) are declared. ************************************************************* From: Robert A. Duff Sent: Thursday, January 23, 2003 7:44 AM If people keep coming up with good ideas for solving the circular dependence problem, it will never get solved. Half :-). ************************************************************* From: Robert I. Eachus Sent: Thursday, January 23, 2003 12:33 PM Pascal Leroy wrote: >I hate the notion that the type structure is tied to the package structure, as >it makes changes extremely painful during maintenance. > >Assume that you have two root library packages P1 and P2 declaring types T1 and >T2. Assume moreover that T1 depends on T2... From one point of view, this is a valid issue, from another it is nonsense. I have always taught that during the design phase of an Ada project, you don't model the solution, you model the problem space. To use the example of a directory system, any solution that makes people subordinate to offices, or offices subordinate to people is wrong. The problem space has offices associated with zero or more people, and people associated with zero or more offices. Any program structure that doesn't reflect this is going to be subject to major restructuring during development and maintenance. (Of course, don't take this to extremes. Current cosmological theories not only permit time travel, they require that causality not hold in all circumstances. But I wouldn't chide a programmer for not allowing negative delays between cause and effect. ;-) package Root is child package P1 is type T1; type AT1 is access T1; end P1; child package P2 is type T2; type AT2 is access T2; end P2; end Root; package Root.P1 is type T1 is record C : P2.T2; end record; ... end Root.P1; package Root.P2 is type T2 is record C : P1.T1; end record; ... end Root.P2; Even if the initial version of the program doesn't need component C of type T2, this structure is clean, easy, and reflects the fact that T1 and T2 are of equal precedence. You might object to a Root package for a system like this, but on methodological grounds, again I will disagree. Whenever you have a situation where several packages must be included in or excluded from a partition as a whole, good design requires that the subsystem be identified as such. ************************************************************* From: Tucker Taft Sent: Thursday, January 23, 2003 1:59 PM I guess I am surprised that large systems don't use a package hierarchy already, with nearly empty "root" packages. But obviously Ada 83 legacy code doesn't do that, but then of course Ada 83 legacy code doesn't have circular dependences ;-). Note that it is relatively painless to introduce a layer at the "top" of a hierarchy, and make everything into a child package. The only changes are the "with" clauses, everything else stays the same, since the top-level name is implicit everywhere. It would be essentially as though package Standard were now spelled "Standard.Top". I also wonder how common it will be to consider introducing a circular dependence a typical "maintenance" activity. Circular type dependence almost always implies some kind of recursive processing as well, so this doesn't seem like a "small" change. I agree it would be great if we could have stand-alone package prefixes, but the impact of that just seemed very large last time we visited it. Perhaps we could design a syntax for stand-alone prefixes, but focus on getting the nested ones approved, and then work on determining the implementation impact of having stand-alone ones. I really think the nested ones are relatively simple to implement, whereas the stand-alone ones imply a new kind of compilation unit, a new kind of "with" clause, new library-item elaboration-order issues, and generally a lot more effort and complexity. ************************************************************* From: Robert A. Duff Sent: Thursday, January 23, 2003 1:59 PM > I guess I am surprised that large systems > don't use a package hierarchy already, > with nearly empty "root" packages. Certainly all the large programs I write have a small number of root packages, and everything else is under those. So are you saying that typically the types in question will already be in some child package P.C, so to make P.C.T mutually recursive with something else, all you have to do is add some code to P, without changing clients of P.C? If so, that seems to address Pascal's concern. > But obviously Ada 83 legacy code doesn't do that, > but then of course Ada 83 legacy code doesn't have > circular dependences ;-). But it wishes it did. ;-) > Note that it is relatively painless to introduce a layer > at the "top" of a hierarchy, and make everything into > a child package. The only changes are the "with" clauses, > everything else stays the same, since the top-level name > is implicit everywhere. It would be essentially as > though package Standard were now spelled "Standard.Top". > > I also wonder how common it will be to consider introducing a circular dependence > a typical "maintenance" activity. Circular type dependence almost always > implies some kind of recursive processing as well, so this doesn't seem > like a "small" change. That's a good point. Generally, it seems like mutually recursive types have to be designed "together" in some sense. Anyway, I would gladly do a lot of work on my code, if there were *any* reasonable solution to this problem. ************************************************************* From: Dan Eilers Sent: Thursday, January 23, 2003 2:44 PM Randy wrote: > Without [tagged incomplete types] you can't do enough with the > incomplete types to make any of these ideas worthwhile. Is it really the case that we're only solving the circular dependence problem for tagged types? There are plenty of examples of inherently circular problem domains that are reasonably modeled using untagged types, such as an Ada parser where the abstract syntax tree nodes are represented using variant records. In Tucker's latest proposal: > package P is > ... > > child package C is > type T; > type T_Ptr is access T; > end C; > ... > end P; This seems a step backwards from the type stub proposal. It isn't much different from package P is ... Type T is separate in P.C; Type T_ptr is access T; ... end P; Both proposals allow the full type in a child package. Neither proposal has a context clause to alert the reader to the reference to a child package. Tucker's proposal is more verbose. It also seems preferable (as it looks like Pascal noted) to have the incomplete type declared immediately within P (as in the type stub proposal), rather than nested in the prefix package C. The type stub proposal is more flexible, in that it allows the full type to be in any designated package. If this flexibility really isn't needed, then the type stub proposal could be similarly restricted. With such a restriction, there might be much less user demand for a context clause, and we might be done. Tucker wrote: > The existing > rules for child packages is that if you see a child unit, > then you always see its parent. Changing that seems > pretty disruptive. Presuming you don't change that, then clearly > the incomplete type needs to be in the parent unit, while the > completion needs to be in the child unit. Well, you have to disrupt something to break the circularity. You either provide a way to disrupt the existing rule that a child unit implies a semantic dependency on its parent, or you disrupt the exiting rule that the parent's spec can't refer to its own unborn child. It seems like a close call as to which is the bigger disruption. Usability would argue that most references be to the parent package. > I do agree that this proposal might reduce the need for separately > compiled private parts, though I haven't really pursued that > investigation. I am mostly trying to come up with a simple > solution to the circularly dependent package problem Yes, first things first. But we do have several very closely related problems to solve, and it would be nice if the solutions are harmonious. If you have a way of deferring the completion of a public incomplete type to a separately compiled child unit, with no semantic dependency on the child unit, then it isn't a very big step to defer the completion of a private type to a separately compiled child unit with no semantic dependency. Similarly, you could also defer the completion of a deferred constant to a separately compiled child unit with no semantic dependency (an issue you raised before but which I believe got lost). ************************************************************* From: Randy Brukardt Sent: Thursday, January 23, 2003 5:23 PM Tucker said (replying to me): >Can you give me a simple example of the visibility >issues you perceive? I am getting mixed up by your >relatively complicated example, and your apparent >desire to declare class-wide operations in the >package prefix. The point of my example was to look at how this would be used in practice, and not in the vacuum of a blank piece of a paper of a hypothetical system. Tucker said later: > I also wonder how common it will be to consider introducing a circular > dependence a typical "maintenance" activity. Circular type dependence almost > always implies some kind of recursive processing as well, so this doesn't seem > like a "small" change. Hogwash! I have an existing application with circular dependencies. It uses various hacks to deal with that in Ada 95. The application has bugs, directly caused by those hacks. (I outlined all of this when I gave the example.) I could build a hack on top of a hack to fix the bugs, or I could use this spiffy new feature to make the circular dependence real, and then those bugs are gone for good. I doubt that this is all that unusual of a situation; Ada programmers have been working around problems of circularity since 1980. Tucker said (replying to me): >> More annoying is that you can't even rename this away at the library level >> (as the rename would clearly refer to the full package, not just this stub). >> You'd have to rename in every package that it is used in. Since I would plan >> to use this in as many places as possible, that is annoying. >I've lost you on the rename issue. Just because a child package has >a prefix, there should be no reason you couldn't rename it. This is a maintenance issue. If you have to introduce a dummy parent package to make this work, then you would like to rename the stub for uses of it alone, and rename the child for uses of it. You can certainly rename the child at the library level: with CBuild_Windows.Root; package CBuild_Root renames CBuild_Windows.Root; but you still have a semantic dependence on this unit. There is no way to get a simple name for the stub alone at the library level. You can rename it inside a scope, of course: with CBuild_Windows; package Whatever is package CBuild_Root is CBuild_Windows.Root; ... end Whatever; But that distributes the change throughout the code. Pascal explained why this is bad better than I can when he said: > Now this means that most of the references to P1 must change to reference > P1_Root.P1. This includes the places that declare objects of type T1 or > otherwise need to view the full type declaration for T1, as well as places that > need to reference operations on T1. True, you can alleviate this somewhat by > providing library-level renamings or skins, but that's still a lot of work for a > rather mundane change. When I do that in C++ all I have to do is type the 5 > letters c, l, a, s, s. When I do that in Ada I would have to stand on my head? > Come on! Tucker is surprised by this: > I guess I am surprised that large systems don't use a package hierarchy already, > with nearly empty "root" packages. But obviously Ada 83 legacy code doesn't do that, > but then of course Ada 83 legacy code doesn't have circular dependencies ;-). It doesn't have EXPLICIT circular dependencies, but I bet it has ones that were hacked around. In any case, I've only used a "root" package once, and that was with Claw. In that case, the main issues were avoiding pollution of the user's namespace, visibility into the private parts of types, and providing a place so that all of the interrelated types could be declared (because we didn't have a circularity solution). The primitive operations all had to be there as well, so the spec is 3700 lines and the body 7100 lines. Hardly "nearly empty". In every other case, the entire application was the "subsystem". The Claw Builder is not small; there is 4.5 Megabytes of source code. (I know that there are much larger systems out there.) The parts are interrelated enough that there is very little that could be independent, so there is no reason for a "subsystem" structure. I certainly can see why some systems would be built with empty top-level packages, but I don't like the idea of forcing people to build things that way. Especially when they are not that way already. Bob echoed Tucker: > Certainly all the large programs I write have a small number of root > packages, and everything else is under those. > > So are you saying that typically the types in question will already be > in some child package P.C, so to make P.C.T mutually recursive with > something else, all you have to do is add some code to P, without > changing clients of P.C? If so, that seems to address Pascal's concern. But on a another forum (in unrelated thread), Bob says: > ... It also requires the client to > "with" bits and pieces of the abstraction. That's *sometimes* desirable > anyway, but in many cases, you want the client to get the whole > abstraction in one with_clause. And you don't want to have extra names > polluting the namespace (sometimes you end up needing a name for the > child package, *and* names for some stuff inside it). Which is precisely my (and Pascal's) point. The idea of my original example was to get the package abstract to be a complete replacement for the original, existing package for almost all uses. Then we can use the original name, and make very few changes to the existing program. (Reading in objects in projects would have to be changed a lot, but that's not relevant here.) The only change is to the packages that actually implement the concrete versions of window objects; no other changes are required. Tucker asked: >> package CBuild_Windows is >Why wouldn't this be package (subsystem) named "Cbuild" and then child named Windows? The practical answer is that "Cbuild" is the name of the application, and thus it is the name of the main subprogram. But, even ignoring that, it wouldn't be quite right. The original package was supposed to be called CBuild_Root_Window; I shortened it because the code using it was getting unreadable: CBuild_Root_Window.Show (CBuild_Root_Window.Root_Window_Type'Class (My_Parent.all)); This was sort of OK, because Windows are a bit more equal than other objects in the builder - you can imagine an application without a menu or a font, but a GUI application without a window? But we've lost some explanation, so I'd prefer to reintroduce the 'lost' Windows prefix. (As you pointed out elsewhere, direct visibility would mean that you wouldn't need to give it inside of the set of Windows.) Dan asked of me: >> Without [tagged incomplete types] you can't do enough with the >> incomplete types to make any of these ideas worthwhile. >Is it really the case that we're only solving the circular >dependence problem for tagged types? There are plenty of >examples of inherently circular problem domains that are >reasonably modeled using untagged types, such as an Ada parser >where the abstract syntax tree nodes are represented using >variant records. The flippant answer is that all useful types are Controlled, so by definition they are tagged. :-) The serious answer is that you can use these for untagged types. But for type stubs, the disruption to your program structure would be much worse. For Tucker's package stubs, the disruption to your program structure is going to massive no matter what. Dan also noted: > Both proposals allow the full type in a child package. Neither proposal > has a context clause to alert the reader to the reference to a child package. This is true, but this is true of child packages in general. Tucker is taking advantage of that fact to avoid the context clause. Somehow I doubt that is what the users who complained at SigAda had in mind, but it's a pretty standard practice. If customers complain about something, give them something worse to replace it. :-) This is getting long. In part 2, I'll finish up looking at Tucker's questions about visibility. ************************************************************* From: Randy Brukardt Sent: Thursday, January 23, 2003 6:29 PM > > This also seems to be a pit of visibility issues. We would have a new > > concept of this additional "part" messing up visibility. Clearly, we'd have > > to check for and disallow homographs in the real spec. But we'd also have to > > change the names of the "real" operations, because otherwise all of the > > calls would be ambiguous. > > Again, I have lost you. Why are you putting any operations > in the package prefix? And what makes these operations > more or less real than operations in the full package? I think my putting operations in the package specification stub was a mistake. The purpose of doing that in the type stub case is so that uses of the package did not need to change. Since there are two separate packages, and I do not want to force users to have to "with" both of them, I put class-wide versions of the operations in the abstract package, which would of course dispatch to the "real" operations declared in the package. But that doesn't seem to be necessary in the package specification stub proposal. (I'm calling it that, because that's the syntax we're using.) What I should have written is: package CBuild_Windows is package Root is separate -- I really want to type an 'is' here... type Root_Window_Type is tagged; type Any_Window_Access_Type is access all Root_Window_Type'Class; end Root; end CBuild_Windows; package CBuild_Windows.Root is type Root_Window_Type is limited tagged with private; -- Completion procedure Show (Window : in out Root_Window_Type); procedure Hide (Window : in out Root_Window_Type); function Display_Name (Window : in Root_Window_Type) return String; end CBuild_Windows.Root; with CBuild_Windows.Root; package CBuild_Root renames CBuild_Windows.Root; -- Compatibility renames. Then the uses would look something like: with CBuild_Window; package CBuild_Menu is type Menu_Item is record Name : String (1..20); Action : Action_Type; Dialog : CBuild_Window.Root.Any_Window_Access_Type; -- If Action=Open_Dialog. end record; procedure Simulate_Action (Item : in Menu_Item); end CBuild_Menu; with CBuild_Root; package body CBuild_Menu is procedure Simulate_Action (Item : in Menu_Item) is begin if Item.Action = No_Action then null; elsif Item.Action = Open_Dialog then CBuild_Root.Show (Item.Dialog.all); ... -- Other actions. end if; end Simulate_Action; end CBuild_Menu; which doesn't seem too bad. I don't like the bizarre case of "depending" on CBuild_Windows.Root in the spec. of CBuild_Menu, but not withing it. (And I suspect that the people who complain about the context clause being missing for type stubs would feel the same with this proposal. But I'm beginning to think that is pretty much inherent in this problem...) But it certainly could be lived with. > I don't see any visibility issues, so we are clearly thinking > about this differently. There seems no particular reason to > put anything in a package prefix other than a few type declarations. > Operations seem redundant (though I don't see any technical reason > to disallow them). Well, you later said that you would ban nesting of these, and that a visible one would have to be completed by a visible child. That would be the two worst cases. But... > > Still, it's the visibility rules that worry me the most. > > I claim they are intuitively obvious! Obviously we must > not have the same understanding of how this proposal would > work. > > > I fear that it will > > take two years to figure them out; we won't have a clue about them two weeks > > from now at Padua. I know I can't do it in time for the meeting, I have a > > lot of other AIs that need to be done. The notion of having different > > visibility to different parts of the spec. is scary, and this seems like a > > big, big job in the symbol table of a compiler (even if it isn't a big job > > conceptually). > > Well I don't know anything about your compiler, of course, > but in ours, each declarative region has a sequence of declarations, > each declaration having its own "logical position." All lookups within a > region need to take a logical position "cutoff" which is > the last declaration that is visible from where the lookup is > taking place. This deals with private part visibility, > visibility at the point of a stub, etc. Clearly if you > only "with" the parent spec, then the last declaration visible > in the child package region is the end of its prefix. That's not quite how its done in Janus/Ada, although the basic idea is similar. There also is a visibility flag used to deal with things like homographs in the same scope. My main concern was that grafting on private visibility into parent parts was a huge mess, and bugs are still turning up from time to time. I can't see how this could possibly be any simpler than that. In any case, the rules for scope and visibility are complicated, and I really do not see how this fits in to those. The answer cannot be that there is no impact, because there are many rules dealing with bodies and private parts and the like, and this is very similar to adding another "part". > > So we need a proposal with the visibility details worked out before I could > > even imagine to decide if it really is simpler, rather than just appearing > > that way without the details worked out. > > I will be happy to do that, but I would appreciate some > better understanding of the problems you perceive, > because I fear I will not address these problems otherwise. I admit that there is a lot of FUD in my concerns here. It's just when someone says "it's simple, trust me", my B*S* detector goes off. The pattern with these AIs is that "it's simple" until someone finds the fatal flaw. "Private with" was simple -- until we wrote the rules. "With type" was simple, but proved to be full of holes. "Pragma Overridding" was simple until someone realized we needed "Optional_Overriding" too, and now the private overridding problem has killed it completely. I know that there can be other things going on between the parts. That usually gets us in trouble for other visibility issues, so it going to take a lot of convincing to prove to me that that does not happen in this case. Probably, I'll need to see a fully worked out proposal, including wording, and for Steve B. and Pascal to have OKed it, before I'll believe. All that said, one thing you have not emphasized (and you should) is that this proposal doesn't seem to need the two pages of "availability" rules that the type stub proposal does. I asked you about that and got no straight answer (which gives me no confidence that you've thought about the rest of it in enough depth). But, presuming that is true, that is a significant advantage of this proposal. (The 'availability' rules would be very murky area both for language lawyers and for implementors; if they were right, users wouldn't notice them at all; but it's hard to tell if they're right.) ************************************************************* From: Robert Dewar Sent: Thursday, January 23, 2003 11:19 PM > I guess I am surprised that large systems > don't use a package hierarchy already, > with nearly empty "root" packages. Seeing as many large Ada programs are derived from legacy Ada 83 systems, you should not be so surprised. In fact I have seen very few of the large systems we deal with structured this way. ************************************************************* From: Jean-Pierre Rosen Sent: Friday, January 24, 2003 1:49 AM > I agree it would be great if we could have stand-alone > package prefixes, but the impact of that just seemed > very large last time we visited it. Perhaps we could design a syntax > for stand-alone prefixes, but focus on getting the nested ones > approved, and then work on determining the implementation impact > of having stand-alone ones. I really think the nested ones are > relatively simple to implement, whereas the stand-alone ones imply > a new kind of compilation unit, a new kind of "with" clause, new > library-item elaboration-order issues, and generally a lot more effort and complexity. > Well... Thinking about it. There is no circular dependency problem if all packages are nested within one big package. The only new possibility of this solution is to separate (part of) the specification. Separating the body was of course already possible. The question is: will the people who were unhappy with the nested packages solution be willing to accept this one? ************************************************************* From: Pascal Leroy Sent: Friday, January 24, 2003 3:55 AM Bob E. argued: > project, you don't model the solution, you model the problem space. To > use the example of a directory system, any solution that makes people > subordinate to offices, or offices subordinate to people is wrong. The > problem space has offices associated with zero or more people, and > people associated with zero or more offices. Any program structure that > doesn't reflect this is going to be subject to major restructuring > during development and maintenance. I agree with you for the textbook example of offices and people. However in real life there are roughly a dozen layers of software between classes that represent real-life entities like offices and people, and classes that interface with software infrastructure like databases, UI, network or OS. As you go down these layers, the decisions because harder and harder to make and may have to change over time. One common reason for change is performance: you may have to add a cache somewhere, or to keep an extra pointer on the last accessed object, etc. That can easily add circularities. I ran into a situation like that in C++ a while ago, in the code of our Ada editor. I had two classes C1 and C2, and it was clear from day one that they were dependent on each other. C1 had a component of type C2 and C2 had a component of type C1*. I won't go into the details, but they were part of a data structure implemented using a doubly-linked list. Because of performance problems I decided to change the data structure to use a skip list. To do this I had to change the classes so that C1 had a component of type C2* and C2 had a component of type C1. In C++ this was very easy to do, and, because the classes were designed with the proper level of privacy, the clients were entirely unaffected (although the implementation of C1 and C2 had to be completely rewritten). My point is that this is exactly a maintenance change, it's not that the design was fundamentally broken. And it was really easy to do in C++... PS: One could argue that I could have pointers in both classes and be done with it. But I try to avoid dynamic allocation if possible. And of course pointers create the risk of unwanted sharing, i.e. more bugs. ************************************************************* From: Pascal Leroy Sent: Friday, January 24, 2003 4:01 AM Tuck commented: > I also wonder how common it will be to consider introducing a circular > dependence a typical "maintenance" activity. Circular type dependence > almost always implies some kind of recursive processing as well, so this > doesn't seem like a "small" change. Of course this discussion is similar to "I wonder how many people will be impacted if we introduce this incompatibility". The truth is we don't know and we have no way to know. In general however I think it's bad language design to force users to follow a certain pattern if there is no compelling reason to do so. Also note that circular dependences are not restricted to data structures: you may introduce a circular dependence because (in my example) you add in P2 an operation that takes parameters of type T1 and T2 (assuming that the types are tagged). ************************************************************* From: Pascal Leroy Sent: Friday, January 24, 2003 4:19 AM Jean-Pierre noticed: > There is no circular dependency problem if all packages are nested within one big package. > The only new possibility of this solution is to separate (part of) the specification. > Separating the body was of course already possible. Good point. Actually, for tagged types, there is no circularity problem if you are willing to play games with access-to-class-wide types: package Root is private type Type_1 is abstract tagged null record; type Access_Type_1_Class is access Type_1'Class; -- Similarly Type_2, Type_3, etc. end Root; Now put all your code in children of Root, derive all your types from some Type_n and use Access_Type_n_Class whenever you have to create circular dependences. This is starting to look pretty much like package prefixes to me. I realize that there are differences, but the two approaches share some of the same drawbacks. If we are to design a brand new language feature, maybe it should have clear advantages over what can already be done with Ada 95. Pascal PS: Of course, in Ada 95 there is not much you can do in the case of untagged types. ************************************************************* From: Robert A. Duff Sent: Friday, January 24, 2003 7:18 AM > Now put all your code in children of Root, derive all your types from some > Type_n and use Access_Type_n_Class whenever you have to create circular > dependences. But then you have zillions of downward conversions scattered all over the code. > This is starting to look pretty much like package prefixes to me. I realize > that there are differences, but the two approaches share some of the same > drawbacks. If we are to design a brand new language feature, maybe it > should have clear advantages over what can already be done with Ada 95. By the way, my initial idea for solving the circular dependence problem was based on this trick. Add a way to declare "this abstract type has only one type derived from it". Check at link time. And then make downward conversions implicit -- it's safe, because you know the tag must be correct. So yes, the tagged type trick is related to package prefixes. > Pascal > > PS: Of course, in Ada 95 there is not much you can do in the case of > untagged types. You can do the same sort of trick for untagged types, if you're willing to use unchecked conversions all over. Yuck. ************************************************************* From: Tucker Taft Sent: Friday, January 24, 2003 8:05 AM Jean-Pierre noticed: >There is no circular dependency problem if all packages are nested within >one big package. The only new possibility of this solution is to separate >(part of) the specification. Yes, that is exactly what we are accomplishing. And that is all that is really needed. And note that in general only *one* package needs to have a little bit be physically nested, and that is enough to allow all the others to be completely separately compiled children. >>Separating the body was of course already possible. Right, but that still meant that the package spec was huge, and growing without bound. With having just a nested "prefix," we only need to nest one (or a very small number) of sub-package prefixes physically within the parent package. So the parent package spec does not grow without bound as you add more sub-packages into the circularity. Pascal wrote: > This is starting to look pretty much like package prefixes to me. I realize > that there are differences, but the two approaches share some of the same > drawbacks. If we are to design a brand new language feature, maybe it > should have clear advantages over what can already be done with Ada 95. I don't see this. Nesting a single package prefix could break circularity in many systems, without any additional, artificial, downward (or unchecked) conversions thrown in. That seems like a *clear* (huge? ;-) advantage over Ada 95. The fact that it is accomplished with such a modest change to the syntax and semantics seems like a feature, not a bug. ************************************************************* From: Robert I. Eachus Sent: Friday, January 24, 2003 8:05 AM >My point is that this is exactly a maintenance change, it's not that the >design was fundamentally broken. And it was really easy to do in C++... > And it should be similarly easy in Ada, no question. My point was that, when as in this case you have two types (classes) C1 and C2 that are of equal precedence the program structure has to reflect that fact, or you run into implementation and maintenace difficulties. If you make it visible at a design, not an implementation level that C2 is a component of C1, then your maintenance change requires changing the design. If the design treats the classes as of equal precedence, however, the change you made would occur only (in Ada) in the private parts and bodies and bodies of the packages that exported the two abstractions. This is the real problem that we are trying to address. Right now Ada forces you to choose an asymmetric design, and at least 50% of the time, that design will be wrong. ************************************************************* From: Randy Brukardt Sent: Friday, January 24, 2003 7:51 PM > Now put all your code in children of Root, derive all your types from some > Type_n and use Access_Type_n_Class whenever you have to create circular > dependences. And this is fairly close to what we did in Claw. The difference being that we made the root types visible and added some of the root operations (such as destruction) at this level. That cut down the number of explicit conversions needed dramtically, but at the cost of putting a lot of code in the root. For CBuild, I simply used names rather than links (in part because there is no common parent to use), but that has some problems as noted. ************************************************************* From: Randy Brukardt Sent: Friday, January 24, 2003 8:15 PM Tucker said: > That seems like a *clear* (huge? ;-) advantage over Ada 95. > The fact that it is accomplished with such a modest change > to the syntax and semantics seems like a feature, not a bug. Please stop claiming this is a "modest" change without proof. I remain skeptical, after all "type stubs" were very simple (but ended up with a bunch of strange availability rules). And the impact on elaboration and freezing and visibility remains to understood. You're going to have to show me the wording before I can sign up for any change to visibility. And even if not a word of visibility needs to change (which I sincerely doubt), it is going to be a significant change inside of compilers. It might be easier to add a third part than adding the second, but I doubt it will be very much easier. ************************************************************* From: Ed Schonberg Sent: Sunday, January 26, 2003 8:11 AM The simplest way to avoid freezing and elaboration issues is to restrict the contents of the "child package abstract" (does this have a name yet?) to contain only incomplete type declarations and access type declarations. This is what is needed to solve the circular dependence problem. Anything else can only be a can of worms. I concur with Randy that anything that touches on visibility is a major disruption to compilers, not a neat bounded change. Only a minimalist solution has a chance here. ************************************************************* From: Tucker Taft Sent: Sunday, January 26, 2003 10:20 AM Randy writes, and Ed reiterates: > Please stop claiming this is a "modest" change without proof. Here is not a proof, but perhaps a way of looking at it that might make it seem modest (or at least less frightening). The idea is to make the child into a "normal" nested package, and put its body into a subunit. Now create a stub in the middle of its body, and think of the code preceding the stub as the rest of the spec of the child, and the code following the stub as the "true" body of the child. This situation should approximate the visibility and freezing rules that would be associated with this proposal. For example, imagine the following, which is almost legal in Ada 95. [The thing that is not legal is that we have allowed the deferred incomplete type to be in the visible part of C. This might be a reasonable relaxation of the rules anyway, though that is a separate (but related!) topic for conversation... ;-)]: package P is ... package C is -- this is like the "prefix" of the spec of C type Some_Enum is (Red, Green, Blue); type T(E : Some_Enum); -- deferred incomplete type type T_Ptr is access all T; for T_Ptr'Storage_Pool use ...; function Whatever return T_Ptr; end C; ... end P; -- freezing happens no later than here -- for anything declared in "prefix" of C package body P is package body C is separate; ... end P; separate(P) package body C is -- This is like the "main" part of the spec of C type T(E : Some_Enum) is record X : Integer; end record; procedure Op1(Y : in out T); procedure Op2(Z : in T); package Viewpoint is end; package body Viewpoint is separate; -- Freezing happens here for -- "main" part of spec of C -- And here starts the "body" of C. function Whatever return T_Ptr is ... procedure Op1(Y : in out T) is ... procedure Op2(Z : in T) is ... ... end C; separate(P.C) package body Viewpoint is -- What is (directly) visible here from P.C is what will be -- (selectively) visible here in units that "with" P.C. ... end Viewpoint; As you can see, we have partitioned P.C into three parts, the first part physically nested in the spec of P (the "prefix"), the second part preceding an external viewpoint (the "main" part of the spec), and the third part following the external viewpoint (the "body"). -------- And now for that other conversation -- allowing a deferred incomplete type to be in the visible part seems a reasonable relaxation in general, since all of these type-stub/package abstract/ package prefix are making uncompleted incomplete types more widely visible. They were already visible to child packages in Ada 95, so it probably doesn't represent an implementation burden. I suppose the only problem is that it might be simply a "bug" that the user forgot to complete the incomplete type. But it is a bug that will be caught when the body is compiled, so that seems pretty reasonable. Another similar relaxation that many customers and non-paying users alike have wished for has been the ability to complete a deferred constant in a package body. This is "standard fare" in C and C++, and is extremely useful from a separate compilation point of view. Again, this might mask a bug where the user simply forgot to complete the constant, but it would be caught as soon as the body is compiled. In any case, it seems we certainly want to allow such things in package abstracts/prefixes (if we go that route), since we know the abstract/prefix represents only part of the spec. But it seems that once you go that far, you might as well grant the added flexibility to users of more "mundane" package specs and bodies. ************************************************************* From: Dan Eilers Sent: Tuesday, January 28, 2003 2:52 PM It looks to me like we can combine the "type stub" proposal with Tucker's "child prefix" proposal, and get the best of both worlds, without the negatives of either. The big negative of "type stub" is that the type stub names a unit that isn't previously declared. The big negative of "child prefix" is that it impacts client code by changing all type references from P.T to P.child.T. Only the clients that need the full type (e.g. for declaring objects) should have to say P.child.T. The "prefix" proposal addresses the first negative by: 1) restricting the full type to be in a child unit 2) giving a "forward" declaration of the child in its parent. By requiring the full type to be in a child unit, there is also no complexity associated with the case where the child could be with'd but not the parent. The "type stub" proposal addresses the second negative by declaring the type stub directly within the parent. So if we combine the two proposals, we get: package P is package C; -- or whatever syntax we like for a child unit stub type T is separate in C; type T_Ptr is access T; end P; instead of: -- type stub proposal package P is type T is separate in P.C; type T_Ptr is access T; end P; or: -- child prefix proposal package P is package C is separate type T; type T_Ptr is access T; end C; end P; Note: I am somewhat reluctant to propose using the keyword "separate" in a child unit stub, since "separate" generally implies that the stub can't be with'd. ************************************************************* From: Tucker Taft Sent: Tuesday, January 28, 2003 2:53 PM Unfortunately this doesn't address the problem where all types are called "Object" and the package name is effectively part of the type name. This was considered a common enough idiom at SIGAda 2002 that I really think we need to solve it. I think a structural advantage to the child prefix proposal as is is that it tends to lead to all the packages that participate in a circular dependence being in the same subsystem, rather than just a random collection of root packages. Since it is relatively painless to add a layer to move a set of related packages into a subsystem, this seems like a small price to pay to allow circular dependence. The only changes are in "with" clauses, and perhaps a "use" for the subsystem as a whole. Renames can also be used to make the packages visible with their old library name. Note that we inserted the "Ada" layer into the Ada 83 run-time library, with no significant upward incompatibilities, thanks to library-unit renaming. ************************************************************* From: Dan Eilers Sent: Tuesday, January 28, 2003 5:24 PM Tucker wrote: > Unfortunately this doesn't address the problem > where all types are called "Object" and the package > name is effectively part of the type name. > This was considered a common enough idiom at SIGAda 2002 > that I really think we need to solve it. OK, I worked out an example which makes this more clear. Given the canonical example of Employees and Departments expressed using the original "package abstracts" of AI-10217: package abstract Employees is type Employee; type Emp_Ptr is access all Employee; end Employees; package abstract Departments is type Department; type Dept_Ptr is access all Department; end Departments; with Departments abstract; package Employees is type Employee is private; type Emp_Ptr is access all Employee; procedure Assign_Employee(E : access Employee; D : access Departments.Department); function Current_Department(D : access constant Employee) return Departments.Dept_Ptr; end Employees; with Employees abstract; package Departments is type Department is private; type Dept_Ptr is access all Department; procedure Choose_Manager(D : access Department; Manager : access Employees.Employee); end Departments; This would be expressed in my proposed hybrid type-stub/prefix form as: package Employees is package Extension; -- new child package stub type Employee is separate in Extension; type Emp_Ptr is access all Employee; end Employees; package Departments is package Extension; -- new child package stub type Department is separate in Extension; type Dept_Ptr is access all Department; end Departments; with Departments; package Employees.Extension is type Employee is private; procedure Assign_Employee(E : access Employee; D : access Departments.Department); function Current_Department(D : access constant Employee) return Departments.Dept_Ptr; private type Employee is record dept: Departments.Dept_Ptr; end record; end Employees.Extension; with Employees; package Departments.Extension is type Department is private; procedure Choose_Manager(D : access Department; Manager : access Employees.Employee); private type Department is emp: Employees.Emp_Ptr; end record; end Departments.Extension; A user would probably want to use library-level renaming to avoid having to refer to the Extension child unit in the clients. Calling everything "Object" works fine, resulting in: package Employees is package Extension; -- new child package stub type Object is separate in Extension; type Emp_Ptr is access all Object; end Employees; package Departments is package Extension; -- new child package stub type Object is separate in Extension; type Dept_Ptr is access all Object; end Departments; with Departments; package Employees.Extension is type Object is private; procedure Assign_Employee(E : access Object; D : access Departments.Object); function Current_Department(D : access constant Object) return Departments.Dept_Ptr; private type Object is record dept: Departments.Dept_Ptr; end record; end Employees.Extension; with Employees; package Departments.Extension is type Object is private; procedure Choose_Manager(D : access Object; Manager : access Employees.Object); private type Object is emp: Employees.Emp_Ptr; end record; end Departments.Extension; Reorganizing to use a new root package Directory, we would get: package Directory is package Employees; -- new child package stub type Employee is separate in Employees; type Emp_Ptr is access all Employee; package Departments; -- new child package stub type Department is separate in Departments; type Dept_Ptr is access all Department; end Directory; package Directory.Employees is type Employee is private; procedure Assign_Employee(E : access Employee; D : access Directory.Department); function Current_Department(D : access constant Employee) return Directory.Dept_Ptr; private type Employee is record dept: Directory.Dept_Ptr; end record; end Directory.Employees; package Directory.Departments is type Department is private; procedure Choose_Manager(D : access Department; Manager : access Directory.Employee); private type Department is emp: Directory.Emp_Ptr; end record; end Directory.Departments; which also works fine, except that as you point out, it doesn't allow type Employee and Department to both be named Object. ************************************************************* From: Tucker Taft Sent: Wednesday, January 29, 2003 8:55 AM Here is another step toward simplification, inspired in part by Dan Eiler's recent thoughts. It also happens to be very similar to a proposal consided during the Ada 9X process, with the crucial difference that it would be allowed in the visible part. Basically, the propsal is to allow an incomplete type declaration to use a (singly) "dotted" name for the type being introduced: E.g.: package P is type C.T; -- incomplete type, specifying that -- full type will be in a nested or child -- package. type T_Ptr is access C.T; -- if desired ... end; package P.C is type T is ... -- full type subtype T_Ptr is P.T_Ptr; -- if desired ... end P.C; --------------- That's it, except of course we all seem to agree that the tagged incomplete type is a good idea, and that incomplete types should be allowed to be completed by a private type. The major change relative to the child package prefix proposal is that we have eliminated the new kind of program unit. How does this compare with other proposals? "with type" had the following problems: 1) New kind of "with" clause 2) Dependence that isn't really a dependence 3) with-type clause could appear anywhere, making it difficult to know when to check that the full type existed and matched the with-type. "package abstracts" had the following problems: 1) Required a new kind of program unit and with clause 2) Special rules for elaboration order and corresponding restrictions on what could appear in the abstract, including possibly nested abstracts, etc. 3) Unclear rules about what part of the parent unit the abstract of a child got to see (none, abstract only, full spec). 4) Because a package abstract is optional, it has some of the same headaches associated with optional specs on library unit subprograms. "type stubs" had the following problems: 1) Multiple type stubs for the same type makes it unclear exactly when the check is to be performed that the package completes the type stub. 2) Trouble when all types have the same name (e.g. Object). 3) Lack of mention in a context clause of the enclosing package, leading to a new kind of with clause, leading to a new kind of dependence 4) Type stub and full type didn't have the same fully expanded name, making for tricky rules for when and where the name associated with the type stub could be used to represent the full type. Subtle "availability" rules. "child package prefixes" had the following problems: 1) Syntax suggests that feature would make sense for root packages, but a seemingly "arbitrary" restriction only allows prefixes for child (or nested) packages. --------------- This proposal does not have any of the above problems, though of course it does have some problems. Here are some of the problems, with some of the remediating factors: 1) It restricts this new capability to types that are in child or nested packages. Remediating factor: Since types are not separately compilable, it creates less of an expectation that the feature should work for types in root packages. Furthermore, it is unlikely the user will think of it that way. Rather, the way it reads to a user is that in the past, I could use incomplete types to create mutually recursive types in a single package. Now I can use these new more flexible incomplete type declarations to create mutually recursive types that involve a package and its child or nested packages. 2) It introduces the name of a child/nested package as part of an incomplete type declaration, leading to a "ghost" package (as Pascal has called it). Remdiating factor: This ghost package exists only in the scope where it will ultimately appear for real as a nested or child package, rather than having to be constructed at each "with-type" clause. One of the problems with the ghost packages required by the with-type clause was that different units might have different sets of with-type clauses, so this ghost package might have more or fewer incomplete types declared inside it, depending on the number of with-type clauses in scope. And then if you "with" the "real" package, it somehow has to be merged with and/or checked against the various ghost packages. In this proposal, the ghost package is created at the first such incomplete type declaration, and only can grow if there are more incomplete type declarations appearing in the enclosing package spec. There is never more than one "ghost" package for a given nested/child package, and every one sees the "same" ghost package. 3) There is no way to "pre"-declare an access type that ends up inside the nested/child package. Remediating factor: You can declare the access type in the enclosing package, and then use a subtype or derived type declaration to put an equivalent/interconvertible access (sub)type into the nested/child package, if desired. As usual, fire away... ************************************************************* From: Arnaud Charlet Sent: Wednesday, January 29, 2003 9:11 AM This last proposal has definitely many interesting capabilities and addresses several issues. However, I don't think (or at least I don't see how) it addresses the issue of generating "bindings" to API written in existing languages. Suppose you have e.g. two header files, or two java classes, not under the same hierarchy, with type definitions referencing one another. How would you automatically and "naturally" translate these with your proposal ? ************************************************************* From: Robert Dewar Sent: Wednesday, January 29, 2003 9:46 AM That certainly seems like an appropriate criterion. Certainly we want a solution that works at least as well in this regard as the current version of WITH TYPE that we have implemented, and routinely use for this purpose. ************************************************************* From: Pascal Leroy Sent: Wednesday, January 29, 2003 9:58 AM Agreed, and that's what makes me uncomfortable with the notion of tying incomplete types with child units. In real life you have many constraints on how you organize your compilation units, and Tuck's proposals all add yet another contraint. I fear that in a number of cases this extra constraint will make the set of solutions empty. ************************************************************* From: Ed Schonberg Sent: Wednesday, January 29, 2003 10:08 AM But why is the incomplete declaration tied to a child unit? Tucker proposes: > Basically, the propsal is to allow an incomplete type declaration to > use a (singly) "dotted" name for the type being introduced: > E.g.: > package P is > > type C.T; -- incomplete type, specifying that > -- full type will be in a nested or child > -- package. I don't see why a child ghost is preferable to an arbitrary ghost! The declaration introduces the name of a unit that will eventually be compiled and provide the completion for the type. Why the restriction to a member of the family? ************************************************************* From: Pascal Leroy Sent: Wednesday, January 29, 2003 10:08 AM > That's it, except of course we all seem to agree that the > tagged incomplete type is a good idea, and that incomplete > types should be allowed to be completed by a private type. Tagged incomplete types are certainly a good idea. Completing an incomplete type by a private is an appealing idea from a language design standpoint, but it frightens the implementer in me. In essence, I fear that having three-part types is going to break a lot of invariants in implementations. We have a zillion places in our compiler where we do a very complex analysis to decide what we know about a type at a particular place (i.e., whether we see the partial view or the full view) and I cannot believe that other compilers don't have similar contraptions. Changing this to deal with three-part-types is going to be a lot of work. Ideally I would love to merge the partial view and the incomplete view (and stick to two-part types), but I have not been able to come up with sensible ideas on how to do that. ************************************************************* From: Tucker Taft Sent: Wednesday, January 29, 2003 10:31 AM Ed Schonberg wrote: > Pascal writes: > >> Agreed, and that's what makes me uncomfortable with the notion of tying >> incomplete types with child units. In real life you have many constraints on >> how you organize your compilation units, and Tuck's proposals all add yet >> another contraint.I fear that in a number of cases this extra constraint will >> make the set of solutions empty. I have suggested that it is always relatively "easy" to introduce an extra layer at the "top" of a (sub)system, as indicated by our introduction of "Ada." in Ada 9x. Can you give some examples where this is not so easy, so I can appreciate the issue better? > But why is the incomplete declaration tied to a child unit? >... > > I don't see why a child ghost is preferable to an arbitrary ghost! The > declaration introduces the name of a unit that will eventually be > compiled and provide the completion for the type. Why the restriction to > a member of the family? Because having multiple ghosts for the same package creates significantly more problems, and the "completing" package is unaware of the existence of these ghosts, and hence checking of appropriate completions has to be performed by users of the type, rather than by the declarer of the type. Both of the proposals I have made recently eliminate any special work on the part of the users of such an incomplete type. All of the work is localized to the place where the type is completed, since it is guaranteed to see and be aware of the incomplete type declaration. ************************************************************* From: Arnaud Charlet Sent: Wednesday, January 29, 2003 10:44 AM > I have suggested that it is always relatively "easy" to introduce > an extra layer at the "top" of a (sub)system, as indicated > by our introduction of "Ada." in Ada 9x. Can you give some > examples where this is not so easy, so I can appreciate the > issue better? I don't think an automated tool that would take e.g. existing C or Java headers would be able to easily introduce an extra layer and put every needed circular dependence in the parent package. This would require a global processing of all possible units, and would mean having to regenerate everything each time a new unit is added. It would also mean lots of unrelated declarations in the parent package. Unless I am missing something, this is definitely not a very attractive solution if you take the foreign language bindings into account. ************************************************************* From: Tucker Taft Sent: Wednesday, January 29, 2003 11:06 AM Robert Dewar wrote: >>Suppose you have e.g. two header files, or two java classes, not under >>the same hierarchy, with type definitions referencing one another. How would >>you automatically and "naturally" translate these with your proposal ? > > > That certainly seems like an appropriate criterion. Certainly we want a > solution that works at least as well in this regard as the current > version of WITH TYPE that we have implemented, and routinely use for > this purpose. I agree that "with type" is the simplest for binding generators, but we have seen the various technical problems associated with this proposal, when we try to nail down full implementation-independent semantics for it. As I mentioned in an earlier note, I personally now prefer "with type" (for incomplete types only, not for access types) to type stubs and package abstracts, but there is still the somewhat tricky rules required having to do with who checks that the full type exists and matches the with-type (i.e. is tagged if specified), and the issue of multiple distinct package "ghosts" that have to be correlated. In any case, I believe that a binding generator for Java could relatively easily add the appropriate incomplete type declarations (required by this newest proposal) into the enclosing package when necessary. Certainly in Java it is absolutely standard practice to put every class of any importance in an enclosing package, so there would always be a place to "drop" these incomplete type declarations in the generated Ada. It might be a bit of an "art" to put as few incomplete type declarations as possible in the enclosing package, but that is more of an optimization than a requirement. For C, it would probably be natural (or at least not unnatural) to create a binding which involved multiple C header files as a package hierarchy, with a root package where these incomplete type declarations could be dropped as needed. In C++, namespaces correspond to Java's classes, and it is becoming more common to declare C++ classes in namespaces, so again, the package representing the namespace would be a natural place to declare the incomplte types for ".Object" or whatever naming convention is used for C++ classes. Note that in C and C++, it is probably more "obvious" than in Java when an incomplete type declaration is needed, since C/C++ have a corresponding notion of incomplete type references. A type from another header that is referenced with such an incomplete type reference would be an obvious candidate to be have an Ada incomplete type declaration for it placed in the enclosing package. I don't think it is a bad tradeoff to make binding generators a bit more complicated (or perhaps even require a small amount of manual fixup) to keeping the language and the compilers simpler. For me, at least, my two favorite proposals are now "with type" (for incomplete types only) and this newest one. They both have the nice effect that there is exactly one fully expanded name for the type, so those using types all called "Object" or whatever have no problems. Also, we don't have to specify complex rules about when one name means the same thing as some other name for the "same" type. This newest proposal has the added benefit that there is at most one incomplete type for a given complete type, rather than one created for each "with type." Furthermore, the complete type knows that the incomplete type is visible to other units, in as much as that might affect representation of pointers (given the possibility of pointer type conversion). Admittedly, it makes binding generators more complicated, but I believe it simplifies implementations, and avoids introducing a new kind of context clause or a new kind of unit dependence. ************************************************************* From: Dan Eilers Sent: Wednesday, January 29, 2003 12:08 PM Arno wrote: > However, I don't think (or at least I don't see how) it addresses the issue > of generating "bindings" to API written in existing languages. > > Suppose you have e.g. two header files, or two java classes, not under > the same hierarchy, with type definitions referencing one another. How would > you automatically and "naturally" translate these with your proposal ? You would basically have two choices: 1) invent a parent to hold the type stubs; or 2) split the package into two parts, one part being the parent of the other, with the type stubs in the parent, and use a library-level renames to rename the child to what you want the clients to use. Robert Dewar wrote: > That certainly seems like an appropriate criterion. Certainly we want a > solution that works at least as well in this regard as the current > version of WITH TYPE that we have implemented, and routinely use for > this purpose. Well, the nicest solution for the user is just to allow specs to with each other, as other languages do. With type is the second nicest user solution, but was abandoned a long time ago for a number of reasons (see Randy's enumeration). Separately compiled package abstracts are the third nicest user solution, but they have tool impact due to a new kind of compilation unit. That leaves putting the abstract either in a parent, or in a child, or in an unrelated unit. Putting the abstract in an unrelated unit prevents having to invent a parent, but was rejected by users at the SigAda meeting, for violating the rule that you can't normally refer to an undeclared unit. Putting the abstract in a child unit also prevents having to invent a parent, but violates the rule that you can't normally compile a child unit before compiling its parent. Putting the abstract in a parent unit is the current proposal. It is probably the least convenient for the user of the proposed solutions, since a parent generally has to be invented or the child renamed, but it is the most consistent with Ada's general design principles. Ed Schonberg wrote: > I don't see why a child ghost is preferable to an arbitrary ghost! Because it is consistent with the Ada design principle that child units are the mechanism for package extension. ************************************************************* From: Jean-Pierre Rosen Sent: Wednesday, January 29, 2003 12:37 PM Since we are furiously inventing... (I thought I submitted this before, but since nobody reacted, it must have been lost - or is it that stupid that nobody even cared to respond? :-). Taking back the package abstract idea, but as a context clause: with package Foo is type T; end Foo; package Bar is.... i.e. each user of a package abstract must repeat it as a context clause, therefore avoiding the new compilation unit issue. There would be a burden at link time, since it is necessary to check that the actual Foo fulfills the promises of all package abstracts. Note that different packages may choose different abstracts from Foo (if it provides several types f.e.) which may not be a bad thing. I.e. each unit makes its own abstract of a complex package, importing only what's needed. No restriction to child unit, so it would address the concern of Java specs. ************************************************************* From: Randy Brukardt Sent: Wednesday, January 29, 2003 2:02 PM > package P is > > type C.T; -- incomplete type, specifying that > -- full type will be in a nested or child > -- package. > > type T_Ptr is access C.T; -- if desired > > ... > > end; I dislike this syntax, for the simple reason that it requires a change to Type_Declaration, rather than to Type_Definition. That ups the ante for implementation considerably (Janus/Ada at least, processes those two productions separately). Let me suggest another syntax: type T is separate in C; :-) This immediately shows us that this is type stubs with restriction on the stub package; nothing more and nothing less. (Indeed, this is slightly better than Tucker's proposal, because there are no ghost packages.) The restriction seems to buy us two things: 1) Since the completion has to (by definition) see the stub, the completion rules are substantially simplified. (Perhaps to non-existent.) 2) Since the completion has to be in the child package, and child packages already can appear without any notation in their parent, we've defined away the dependence in the context clause concern. (I doubt that will really make the people who complained happy, but that's the solution.) Of course, it also continues to have the problem of forcing a design into child packages whether that is desired or not. We could get the advantages of (1) and not have the requirement to force the design, by requiring that the completion with the package containing the stub. That would have to be a post-compilation rule (unfortunately), but it would eliminate virtually all of the completion complexity. [While it would be possible for packages with different visibility on the stub to meet, since withs are non-transitive, we know that withs are transitive for the purposes of type matching. Thus, anything with visibility on the completion would have "know" about the stub, so type compatibility would remain as it currently is in implementations (such as a compare of type numbers in Janus/Ada).] Of course, that does not do anything to address the user concern about showing dependence in the context clause. That could be addressed as in alternative #5, or we could declare it a red herring and move on (as some people here seem to prefer to do). ************************************************************* From: Randy Brukardt Sent: Wednesday, January 29, 2003 2:10 PM > That certainly seems like an appropriate criterion. Certainly we want a > solution that works at least as well in this regard as the current > version of WITH TYPE that we have implemented, and routinely use for > this purpose. But it's also an impossible criterion. I don't think that any of the proposals considered (INCLUDING with type) meets it. That's because the GNAT with type allows withs of access types, which we had to eliminate from any proposal because of various problems. And I expect that would be the keystone to any "easy" importation of Java or C++ classes. Therefore, with ANY proposal that the ARG has considered requires the introduction of a second package - either an abstract (or prefix), or a real package containing a type stub (which includes Tucker's most recent "parent" proposal). And thus, the translator of the classes is going to have to do some restructuring. In any case, I think it is important that we not lose sight of the fact that whatever we choose needs to be natural in an all-Ada design. Ada programmers have had this problem for 20 years, and generally have had to bloat up packages to deal with it. It would be pretty silly to adopt something simply because it makes copying Java classes easy (unless we believe that there won't be any new Ada designs -- in which case, we're all wasting our time on this whole exercise). ************************************************************* From: Tucker Taft Sent: Wednesday, January 29, 2003 3:10 PM > I dislike this syntax, for the simple reason that it requires a change to > Type_Declaration, rather than to Type_Definition. Is this really that big a deal? Compared to all the other options? > ... That ups the ante for > implementation considerably (Janus/Ada at least, processes those two > productions separately). Let me suggest another syntax: > > type T is separate in C; > > :-) But what does this mean? Is the full name for the type "P.C.T" or is it "P.T"? The fact that I have to ask this question makes me think it is an error prone syntax. > This immediately shows us that this is type stubs with restriction on the > stub package; nothing more and nothing less. > > (Indeed, this is slightly better than Tucker's proposal, because there are > no ghost packages.) I have lost you. Don't you still have to create "C", or are you saying the full name of the type is "P.T"? If the latter, then you are back to the situation where, presumably, in some cases P.T is an incomplete type, and in other somewhat-difficult-to-define cases, P.T is a "complete" type. Or are you saying that P.T is *always* an incomplete type, even if you "with" P.C? To me it is this issue of when or if "P.T" is a complete type that is the fundamental problem with type stubs which introduce their own name. > The restriction seems to buy us two things: > > 1) Since the completion has to (by definition) see the stub, the > completion rules are substantially simplified. (Perhaps to non-existent.) > 2) Since the completion has to be in the child package, and child packages > already can appear without any notation in their parent, we've defined away > the dependence in the context clause concern. (I doubt that will > really make the people who complained happy, but that's the solution.) I presume you are agreeing that the "type C.T;" proposal has the same two advantages. > Of course, it also continues to have the problem of forcing a design into > child packages whether that is desired or not. > > We could get the advantages of (1) and not have the requirement to force the > design, by requiring that the completion with the package containing the > stub. This sentence no verb. ;-) > ... That would have to be a post-compilation rule (unfortunately), but it > would eliminate virtually all of the completion complexity. [While it would > be possible for packages with different visibility on the stub to meet, > since withs are non-transitive, we know that withs are transitive for the > purposes of type matching. Thus, anything with visibility on the completion > would have "know" about the stub, so type compatibility would remain as it > currently is in implementations (such as a compare of type numbers in > Janus/Ada).] Unfortunately, I cannot understand this idea presumably due to lack of some key words in the sentences. > Of course, that does not do anything to address the user concern about > showing dependence in the context clause. That could be addressed as in > alternative #5, or we could declare it a red herring and move on (as some > people here seem to prefer to do). What is alternative #5, in a few words? Other than the detailed syntax, are you saying you like the semantics of "type C.T;", or are you saying you prefer the restricted-to-children type stub where the full name is "P.T;" of the incomplete type, while the complete type name is "P.C.T"? ************************************************************* From: Randy Brukardt Sent: Wednesday, January 29, 2003 4:21 PM > Unfortunately, I cannot understand this idea presumably due to lack > of some key words in the sentences. The only words that I saw that were missing was "is". I've been working on the search engine, where words like that are ignored, and I've taken it a bit too far... :-) Anyway, the basic idea is that P.T is always incomplete; P.C.T is complete and matches P.T trivially because it always knows about P.T. Thus the completion rules are simplified. (That does mean that P.T and Q.T don't match each other if both are stubs of the same completion; I don't think that matters in any of the canonical uses we've sketched out.) I definitely didn't work out the details on this one - one fully worked out alternative per meeting is my limit. :-) > > Of course, that does not do anything to address the user concern about > > showing dependence in the context clause. That could be addressed as in > > alternative #5, or we could declare it a red herring and move on (as some > > people here seem to prefer to do). > > What is alternative #5, in a few words? Some sort of context item to introduce a package name solely for use in type stubs. I had proposed abstract of P; but the semantics are the same no matter what the syntax. with separate P; works as well. > Other than the detailed syntax, are you saying you like the semantics > of "type C.T;", or are you saying you prefer the restricted-to-children > type stub where the full name is "P.T;" of the incomplete type, while the > complete type name is "P.C.T"? I think I like original type stubs the best. :-) I was just trying to figure out if your proposal was significantly different than type stubs, and it didn't seem like it. If I had to rank these by alternative number (and giving prefixes #6 and this latest one #7), I'd say: 4 (type stub alone), 5 (type stub with context marker), 7, 6, 1 (fixed up with type, any syntax). ************************************************************* From: Tucker Taft Sent: Thursday, January 30, 2003 8:32 AM There seems to be some feeling that type stubs were almost right, and we should either leave them as is, or add some kind of special context clause such as "separate with ..." and be done with it. Personally, I don't feel that way anymore. Here are some reasons why: 1) Type stubs look and feel like an add-on to the language. Nothing about them looks natural to my eyes. As a language designer/enhancer, I always want the new features to look like they were "always" there, so the language is seamless and there is no obvious dividing line between old and new features. 2) Type stubs are not necessarily "seen" by the type completer. I think this could have negative implications for compilers like GNAT that have multiple representations for access types (access-to-unconstrained array can be thick or thin, and I presume there is no easy way to convert a thick pointer to a thin one). Of course GNAT itself doesn't have this particular problem with their existing "with type" implementation, because they "peak" at the actual type declaration at the time of processing a "with type." I suppose this same trick could be used for type stubs, but requiring "peaking" at the full type seems generally unwise, and has interesting implications from a recompilation point of view. 3) Type stubs seems to require a particular "idiom" of use that makes it seem clear that some kind of package "prefix" or "abstract" is what we *really* wanted but couldn't somehow bring ourselves to provide. To be specific, we have said that to deal with cases where most types have the same simple name (e.g. "Object"), you should use an idiom where a separate package is created to hold the type stub. In fact, there seems to be some sense that this is a good idiom even if the type names aren't all the same. If this is really true, then it is clear to me that the type stub feature we designed is not the right solution, and a package prefix/abstract would be better. 4) Type stubs have the unfortunate situation where the same type has two different fully expanded names. This creates numerous problems and confusions for specifying the rules, implementing the rules, and understanding the rules relating to when the two types are the same and when they are different. With the other approaches, there is only one fully expanded name for the type, and the usual 8.3(19) hiding rule that says a completion hides the prior declaration from all visibility within its scope, will generally be adequate to decide whether you have an incomplete type or a full type. -------------- Of the other approaches that still seem viable to me, "with type," package prefix/abstract (including the stand-alone case), and "type C.T;" incomplete types: a) "with type" has odd dependency semantics (dependence without a real dependence) and the completer doesn't "see" the incomplete type(s) b) package prefix/abstract feels like a big change, requiring a new kind of program unit, a new kind of context clause, and non obvious rules about what part of the parent unit a child prefix/abstract can see. Admittedly, the package prefix is probably the most powerful and flexible. c) "type C.T;" seems like a natural outgrowth of two basic facts: i) the incomplete type declaration is the fundamental way of supporting mutually dependent types in Ada, but it is limited to only working within a single package spec; ii) child packages are the fundamental way of splitting a large abstraction into smaller, more manageable parts, but they are limited in that mutually dependent types cannot cross these pieces of the abstraction. Obviously the above analysis is biased by what feels to me to be the simplest "good" solution. The major objection to the more flexible incomplete type declaration seems to be that it might not just "drop" into existing code. But there is no existing code that actually solves the mutually dependent type problem. There are only a collection of workarounds. It seems unwise to require that any "real" solution to the problem be able to simply drop easily into all existing workarounds. There is no rule that the existing workarounds have to overnight switch to using every new feature that might have obviated the workaround. It is like in Ada 95 -- clearly tagged types are a nice solution to the problem of variant records and case statements growing larger and larger as a data abstraction gets more complex. But to take advantage of tagged types, you really have to restructure everything. So there are plenty of people still using variant records, for historical reasons, or because they are still waiting for the mutually dependent type problem to be solved before they "jump" to tagged types. I don't think that means we should have designed tagged types so they could easily slip into all code that was using variant records before. We have to be focused on the person developing a new abstraction, and how the language can be enhanced in modest ways to enable that person to develop the abstraction without having to stand on their head. The old abstractions that were built by standing on your head will probably always look like that, until there is the time and money to redesign them to take best advantage of new language capabilities. My final word is to try to answer "why the change of heart"? Presenting the various Ada 200Y proposals to SIGAda '02 was generally a reconfirming experience, making me feel that we were generally on the right track. However, the type stub proposal received more negative reaction than anything else. This made me step back a bit from the proposal and try to look at it with the perspective of an outsider. I concluded that this proposal seemed to be suffering from the results of "group think." Group think is where a relatively isolated group (e.g. the ARG) spends a very long time struggling to find a solution to a problem, and ultimately convinces themselves, perhaps out of exhaustion, that they have found the right solution. But when someone new steps into the room, that new person immediately recognizes the solution for what it is -- a dog. The group will generally try to pooh-pooh the outsider's view, feeling that they just don't understand all the complexities of the problem, or missed all the relevant discussions that went on arriving at the solution, and if the group is unlucky, they will ultimately dismiss the outsider's perspective. I don't think we can afford to do that. We really want the changes to Ada to stand the test of time, so years from now someone will look at Ada 200Y as a whole and think that it is a good, well integrated, internally consistent language, without a lot of ugly baggage hanging off the side due to periodic "maintenance" activities. Obviously, given our current resources, we don't want to waste implementors' or users' time with enhancements that won't stand the test of time, and have to be "re-maintained" many times in the future to get them right. There were a few dogs in Ada 95 (like streams), and it has been painful to straighten them out. Luckily, for most of these dogs, the core idea seems to have been good, even if the details were a shambles. Anyway, so much for philosophizing... ************************************************************* From: Jean-Pierre Rosen Sent: Thursday, January 30, 2003 10:06 AM > Group think is where a relatively isolated group (e.g. the ARG) > spends a very long time struggling to find a solution to a problem, > and ultimately convinces themselves, perhaps out of exhaustion, > that they have found the right solution. But when someone new steps > into the room, that new person immediately recognizes the solution > for what it is -- a dog. The group will generally try to pooh-pooh > the outsider's view, feeling that they just don't understand all > the complexities of the problem, or missed all the relevant discussions > that went on arriving at the solution, and if the group is unlucky, > they will ultimately dismiss the outsider's perspective. I don't > think we can afford to do that. We really want the changes to > Ada to stand the test of time, so years from now someone will > look at Ada 200Y as a whole and think that it is a good, well integrated, > internally consistent language, without a lot of ugly baggage > hanging off the side due to periodic "maintenance" activities. > Are you thinking of unreserved keywords here? (sorry, couldn't resist) ************************************************************* From: Randy Brukardt Sent: Thursday, January 30, 2003 4:01 PM Tucker said: > ... As a language designer/enhancer, I always want the new features to look > like they were "always" there, so the language is seamless and there is no > obvious dividing line between old and new features. That's clearly an impossible goal in this case, and using it as a criteria is going to lead you nowhere. The reason for that we're trying to do something unnatural to the basic design of Ada: use something before it is declared. In order to do that, we have to create some feature that allows using something that isn't even declared yet. In order to make that implementable without major disruptions (and that is a requirement politically, if not technically), it has to be heavily restricted. But heavily restricting something is by itself unnatural: usually, if you can declare something in one place, you can declare it somewhere else as well (assuming appropriate visibility). Moreover, any of solution of this sort requires making some subset of "class" visible prematurely. And that, is, of itself, unnatural and potentially error-prone. (At least Ada will check for problems at compile-time, but its better to not have to do that in the first place.) Any 'natural' solution would have to get rid of special cases and probably eliminate most of the compilation order rules. But any such solution would not be in the spirit of Ada. To make the point clear, here is a quick look at every alternative offered and why its unnatural (I'll do them in proposal order to avoid bias, and I'm going to try to avoid any technical issues): 1) With type. Unnatural because it's the only context clause item on non-program units. Unnatural because its restricted to incomplete types only. Why not private types or constants? 2) Package abstracts. 3 package parts aren't enough apparently, so let's add a fourth. Sheesh. This is a clear violation of the zero, one, or many rule of good design. What goes in the extra part certainly isn't clear or natural - you'd put whatever you had to in there to make the design work. 3) Old type stubs (explicit completions). The idea of a stub is natural enough, but limiting its use to very restricted cases isn't. The completion syntax is new, but that hardly makes it unnatural. (Humm, I can't figure out why this one would be unnatural. Too bad everyone hates it. I note that it eliminates two of Tucker's concerns as well...) 4) Current type stubs. As with the previous case, stubs are natural, restricted cases aren't. The concern about having an undeclared dependency remains. The limitation to the same name is unnatural, but is easily fixed. 5) Type stubs with a context clause. It's unnatural to use something before its declared, whether in the context clause or anywhere else. Otherwise, the same as previous. 6) Child package prefixes. These are unnatural because they're limited only to the library level of library packages - no higher and no lower. And, since they're just warmed over package abstracts, they have all of the unnaturalness of that solution as well. 7) Type C.T. This is the most unnatural of all. It has most of the faults of both type stubs and package prefixes. First, we're declaring an expanded name. This is new for non-program units. OK, but then why is it restricted to just incomplete types? Why not deferred constants and by-reference objects and private types ...? You're still forced to use these only at the library level of library packages (otherwise it couldn't be a child), greatly constraining the design. And you still have to figure out when they need to be declared separately - and I expect that will occur only when its clear that the design won't work any other way. Making people redo their design because a need for mutual dependence arises is no better than making them redo their existing code. The real point is that it is not a language designer's job to decide how programs ought to be designed, whether they're new, existing, or whatever. It's the language's job to give the designers the tools to do that job. No one on the ARG can say how Ada programs really are going to be designed using these features -- at best, we can only guess. We shouldn't constrain those designs unless we really have no choice. (And we shouldn't be saying that people can't use "in out" parameters on functions or have a function with no return statement, either. But that's a different story.) Type stubs have the nice property that they can be used wherever they are needed: in library specs, in private parts, in nested packages, even in a protected entry body if the need arose. That gives maximum flexibility to the designer. Tucker's philosophy seems to be that 'new designs' should be sets of deeply nested child packages. I've tried that design (with Claw), and was not very pleased with the results. The primary problem is the package names get very long. For me, as a use-adverse programmer that uses full dot notation on everything except items declared in the local source text (and its spec, with limits) [with very local use/use type clauses for sanity], the clutter is awful. That's why CBuild was designed to be mostly flat. Similarly, the temptation to use stuff from the parent's private part is strong, and impossible to check for. Why allow that? (Of course, if you NEED it, then go ahead.) So, for me, (mostly) flat is better. Your mileage may vary. But I certainly do not want the language saying: "You have to use deeply nested designs. You have to either use "use" clauses or (worse to my view) direct visibility from source files that you don't even have open in your editor. Or you have to junk all of your current programming tools to use a fancy toolset that will let you find declarations in code that you haven't even compiled yet (and won't compile, either)." OK, enough philosophizing. (Gee, is there an echo in here??) :-) >2) Type stubs are not necessarily "seen" by the type completer. I suggested a possible fix for this yesterday. I do agree that that would be a good idea, but it isn't necessary. The original type stub proposal (with explicit completions) clearly does not have this problem (it has other problems, of course). > I think this could have negative implications for compilers > like GNAT that have multiple representations for access types > (access-to-unconstrained array can be thick or thin, and I > presume there is no easy way to convert a thick pointer to a thin one). This is an existing problem in Ada 95, as you can do this in a package spec. deferred to a body. Whatever solution works there should work here. I believe GNAT peeks, as you said, and it would have to do that for type stubs. > ...To be specific, we have said that to deal with cases where most types have > the same simple name (e.g. "Object"), you should use an idiom where a separate > package is created to hold the type stub. There is not the slightest problem in fixing this problem; the ARG decided NOT to fix this problem. That was a mistake then, and is still a mistake. The type stub syntax ought to be something like: type T is separate of P.Object; When I originally proposed this, I certainly had no intention on limiting the names used, and I still think its an arbitrary restriction that causes nothing but trouble. But I was out-voted in Cupertino. C'est la guerre. > Type stubs have the unfortunate situation where the > same type has two different fully expanded names. What are you talking about? Every entity in Ada can have a multitude of names. The names are completely irrelevant. To the extent that type stubs talks about names, THAT is a problem. We're only interested in the entities matching. In Janus/Ada at least, there is no difference if the names are the same or different. Every declaration makes a unique type slot, so incomplete types and private types are different than the completing types. Whether or not you're allowed to treat them as the same is handled by a bunch of complex rules. And certainly, that would remain true in every proposal made here. > We have to be focused on the person developing a new abstraction, > and how the language can be enhanced in modest ways to > enable that person to develop the abstraction without having > to stand on their head. Absolutely. And, in my opinion, forcing use of "unnecessary" child packages is precisely that sort of standing on your head. If I redesigned CBuild from scratch today, I still would use as few child packages as possible (see above). Being forced to use a lot of them just to make Tucker happy makes no sense to me at all. Tucker, I know you believe that everything should be nested in several layers of nearly empty packages. Fine, do your software any way you want. Can we at least agree that not everyone agrees with your design structure? ************************************************************* From: Tucker Taft Sent: Thursday, January 30, 2003 4:50 PM Randy Brukardt wrote: > > Tucker said: > > > ... As a language designer/enhancer, I always want the new features to look > > like they were "always" there, so the language is seamless and there is no > > obvious dividing line between old and new features. > > That's clearly an impossible goal in this case, and using it as a criteria > is going to lead you nowhere. > > The reason for that we're trying to do something unnatural to the basic > design of Ada: use something before it is declared. But that is exactly what incomplete type declarations are for. They are just too restrictive in scope. In any case, Randy is right that one person's "natural" could be someone else's "dog". To me, extending the incomplete type declaration to allow mutual dependence across child or nested packages is natural, whereas to him it is an abomination. > ... > Tucker, I know you believe that everything should be nested in several > layers of nearly empty packages. Fine, do your software any way you want. > Can we at least agree that not everyone agrees with your design structure? I think this is a bit of an exaggeration, but that is of course the nature of these debates sometimes. If we can't sway people with our best technical arguments, we try emotional appeals (me too, by the way ;-). We may have to move toward the "Getting to Yes" approach of Camp David if we can't come a little closer to consensus on this one. What *can* we agree on, and thereby at least eliminate some of the solutions from the table? Once we get down to a small enough set (hopefully two), we can ultimately just vote (much as I hate losing such votes ;-). Possible points of consensus: 1) All inter-unit dependence must be either child on parent, body on spec, or explicitly mentioned in some applicable context clause. 2) We need a mode of use that works reasonably with the "type Object" paradigm. 3) No new kinds of compilation units (is that still a point of consensus?) Anything else in our consensus? Are the above 3 in the consensus? ************************************************************* From: Randy Brukardt Sent: Thursday, January 30, 2003 5:03 PM Arnaud Charlet wrote: > I don't think an automated tool that would take e.g. existing C or Java > headers would be able to easily introduce an extra layer and put every > needed circular dependence in the parent package. This would require a > global processing of all possible units, and would mean having to regenerate > everything each time a new unit is added. It would also mean lots of > unrelated declarations in the parent package. > > Unless I am missing something, this is definitely not a very attractive > solution if you take the foreign language bindings into account. This is a red herring. As I mentioned earlier, every solution considered requires some sort of unnatural restructuring to use. That's just the nature of the problem: we're trying to do something unnatural to the Ada model. A binding generator simply has to assume that there might be circular dependence (and might as well, given that these incomplete declarations generate no code). That means generating whatever additional structure is necessary to make this circular dependence possible. For the particular proposal you are responding to, clearly every binding would have a wrapper package that contained the incomplete type. A bit ugly perhaps, but hardly a major problem. For type stubs, there would be a separate "interface" package containing the incomplete type and an access type to use everywhere. With type also needs such a separate type package unless you never need a named access type (which seems unlikely given the heavily pointered nature of those other languages). And for a package prefix/abstract, you'd need the prefix. So there doesn't seem to be any advantage to any of these proposals this way. Obviously, you could "optimize" these extra artifacts away, but I think that would be a bad idea, because, as you say, adding another class to the program could change which ones are needed. ************************************************************* From: Randy Brukardt Sent: Thursday, January 30, 2003 5:18 PM Tucker was confused by my sentence: > > We could get the advantages of (1) and not have the requirement to force the > > design, by requiring that the completion with the package containing the > > stub. > > This sentence no verb. ;-) And I couldn't parse it either when I replied. But it does have a verb: "with", in the Ada sense. If I could write these messages in HTML, it would have been in bold and would have made more sense: We could get the advantages of (1) and not have the requirement to force the design, by requiring that the completion WITH the package(s) containing the stub. I don't know if that makes it any clearer, but I hope so. Then we could simplify the completion rules, because the stub would always be available if the completion is. (There may be some simplification possible even without this rule, but I think it would be more natural with it.) I'd pursue this further, but I have a lot of other work I need to do before the meeting. ************************************************************* From: Dan Eilers Sent: Thursday, January 30, 2003 6:04 PM Tucker wrote: > Anything else in our consensus? Are the above 3 in the consensus? I don't know if it rises to the level of consensus, but it seems highly desirable from a usability point of view to require that clients of a circular type not have to change on account of the circularity. That means if there was previously a package Employees, with a type Employee, and Employee was made circular with Departments.Department, then clients could still "with" package Employees, and still refer to type Employees.Employee. Specifically, we can't require the client to use an invented parent unit such as Directory.Employees.Employee, nor can we require use of an invented child unit, such as Employees.Extension.Employee. Nor do we want to require library-level renames to satisfy this requirement. Both the "with type" proposal and the "package abstracts" proposals seem to meet this requirement, as does my proposal to put the "abstract" in a child unit that can be compiled before its parent. The early type-stub proposal allowing the stubs in an unrelated package also works. But the modified type-stub proposal restricting the full type to a child, as well as the package-prefix proposals both fail. The difficulty seems to stem from the fact that child units are the natural Ada way to extend a package, but the client should "with" the main package, not an invented child containing the type completions. A solution we haven't considered would be to allow a package spec to be extendible via package subunits, which would contain the completions of incomplete types in the package spec. The subunits wouldn't be "withable" by clients, but would be automatically included in any "with" of the main package. A different kind of "with" clause would be used to "with" the main package without its subunits (i.e., the abstract of the main package). Something like "with employees'abstract". ************************************************************* From: Randy Brukardt Sent: Thursday, January 30, 2003 6:13 PM (BTW, sorry about answering some old messages a second time. I was filing the mail, and some of the statements bothered me enough that I felt compelled to reply -- not remembering that I did so yesterday. I think 44 is too young to get Alzheimer's. :-) Anyway, Tucker wrote: > But that is exactly what incomplete type declarations are for. > They are just too restrictive in scope. > > In any case, Randy is right that one person's "natural" could be someone > else's "dog". To me, extending the incomplete type declaration to > allow mutual dependence across child or nested packages is natural, > whereas to him it is an abomination. I'd agree with the premise, but the point is that that is exactly what type stubs are supposed to be. Certainly, the original proposal with explicit completion was designed that way precisely so that it was a natural extension of the existing syntax and semantics. And that's still true with the current one: it is just a different name (like a subtype) for the completing type. You'll get all confused if you start paying attention to exactly how that is accomplished. (That happens a lot in Ada; often, you're better off not trying to understand the detailed rules unless you're an implementor or language lawyer.) So I don't see any difference in the user model for either of these proposals, only that type stubs isn't restricted in how or where it can be used, and the Tucker "type C.T;" is restricted. OTOH, it's a bit unnatural that this doesn't work for private types. More generally, the problem is that Ada has several kinds of "restricted visibility types", and really, that should have been a more general mechanism, rather than a series of special kinds of types. Of course, its too late to fix this in any useful way. > > ... > > Tucker, I know you believe that everything should be nested in several > > layers of nearly empty packages. Fine, do your software any way you want. > > Can we at least agree that not everyone agrees with your design structure? > > I think this is a bit of an exaggeration, but that is of course the > nature of these debates sometimes. If we can't sway people with > our best technical arguments, we try emotional appeals (me too, > by the way ;-). The very first response you made to my original type stub proposal was "...and I wouldn't mind requiring it to be a child of the package where the incomplete type is declared,..." (July 9, 2001), so it seems to me that you are always thinking in that direction. (How else could you stomach restricting uses this way?) In any case, I was just trying to make sure that you hadn't lost sight of all of the different ways Ada is used. > ... > What *can* we agree on, and thereby at least eliminate some of the > solutions from the table? Once we get down to a small enough set > (hopefully two), we can ultimately just vote (much as I hate > losing such votes ;-). I fear that there is not much. > Possible points of consensus: > > 1) All inter-unit dependence must be either child on parent, body on spec, > or explicitly mentioned in some applicable context clause. I can agree with this as written, because a type stub doesn't introduce dependence. :-) So, from a language lawyer/implementor perspective, we're fine. But, clearly, there are some people who think that this not-really-dependence needs to be in the context clause. Obviously, we need to address their concern, but there is another alternative to agreeing with this point: re-education. After all, Ada 83 didn't have the invisible child on parent dependence, and it isn't clear from the context clause. Indeed, I often forget that it exists, which is one reason that my personal coding rules do not allow using the direct visibility on the parent to simplify names. So, I don't see any horrible problem with adding another hidden dependence, particularly when its not really a dependence at all (any change to the completing package has no effect on the stub's package, so there is no issue). I really don't care either way, so if there is some strong support for this position, I would be happy to go that way. (Which is why I prepared alternative #5, so we could compare the wording changes needed with or wothout such a context clause.) > 2) We need a mode of use that works reasonably with the "type Object" > paradigm. I've always agreed with this. But I wonder if the whole ARG does, because we voted down making type stubs do this. And I see no reason to require the names to match: we don't do that for other sorts of renaming, why here? > 3) No new kinds of compilation units (is that still a point > of consensus?) I'd rather not have any, but I must admit that it is the only way that I can see to reconcile my viewpoints with yours. But I don't know if we can afford to have new kinds of units. > Anything else in our consensus? Are the above 3 in the consensus? Well, I think we should include: 0) We have to have a solution to mutually dependent types in the Amendment. This is a very important problem which simply has to be solved (even if the solution is ugly). 4) We have to be able to automatically translate headers from C++ or Java into Ada using this feature. I'd probably add, but don't know if it is a consensus point: 5) The model has to be reasonably easy to explain to the average experienced Ada programmer. (Because if we had done that the first time, we wouldn't be talking about it now.) ************************************************************* From: Tucker Taft Sent: Thursday, January 30, 2003 9:53 PM Dan Eilers wrote: > I don't know if it rises to the level of consensus, but it seems highly > desirable from a usability point of view to require that clients of a > circular type not have to change on account of the circularity. For what its worth, thanks to library unit renaming, I don't see any need for clients to change with the "type C.T;" approach. For those situations where child packages just won't do, you could use an approach like the following: package Top is type Departments.Department; type Employees.Employee; ... -- any others needed to break circularity among ... -- top level packages end Top; package Top.Departments is type Department is ... procedure Add_Employee(Dpt : in out Department; Emp : access Employees.Employee); ... end Top.Departments; with Top.Departments; package Departments renames Top.Departments; package Top.Employees is type Employee is ... procedure Set_Manager(Emp : in out Employee; Mgr : access Managers.Manager); ... end Top.Employees; with Top.Employees; package Employees renames Top.Employees; Essentially, this is using "Top" as a package where you can place the incomplete type declarations for what are intended to be top-level library units, if they are needed to break circularity. [In this case we have placed both Departments.Department and Employees.Employee into Top, but you could normally get away with just one of them, since breaking the circularity only requires one incomplete type declaration.] It is then expected that all children of "Top" will be renamed to be top-level library units for use by clients. If you generally design using package hierarchies, then rarely will you have to use the "Top" trick. E.g., both Departments and Employees might have already been children of package Office, or Departments might have been a child of Employees. But in a pinch where you need mutual dependence between packages that are not inside the same package hierarchy, using a single "Top" parent for all such packages would work. It is almost as though you were able to add incomplete type declarations into the spec for Standard. In fact, we could consider treating an explicit declaration of a package Standard as meaning "these are declarations to add at the end of my package standard," but that would be incompatible with all those folks out there who really do have a library package called Standard (Standard.Standard, that is). I suppose compiling a file containing only pragmas is similar to placing the pragmas in package Standard, so this would be somewhat similar to library-wide configuration pragmas. Of course, you could also have a separate "Top"-like package for each logical group of packages that have a mutual dependence, but that begins to smell like a package hierarchy again ;-). ************************************************************* From: Robert A. Duff Sent: Friday, January 31, 2003 12:01 PM Randy said: > Well, I think we should include: > > 0) We have to have a solution to mutually dependent types in the Amendment. > This is a very important problem which simply has to be solved... Well said. I would be perfectly happy with the original ARG-approved proposal that WG9 rejected. There were two concerns: - It seems bad to mention package names that aren't 'with'-ed. A perfectly legitimate response is: Yes, this is a legitimate point, but ARG is unable to fix the problem in the available time, and anyway, it's hardly a big deal. The other alternative is to add a new kind of with clause that does *not* introduce a dependency (in fact, it doesn't do much of anything except suppress a certain error message). That's reasonable too, but I don't think we *have* to do that. - The "every type called Object" issue. Well, it seems to me that when you choose this style, you are limiting yourself to one type per package (or at least one type called "Object" per package -- presumably the "main" type). So you need to create packages to hold all the type stubs. So what? Tucker doesn't like that proposal, for aesthetic reasons he has been unable to articulate to *my* satisfaction. Fine. I'm also perfectly willing to go along with Tucker's recent "type C.T;" proposal. Tucker has demonstrated that it is possible to add cycles without editing clients, even in systems that do not currently use child units. That should address Dan's concern. I also don't buy Randy's objections -- you don't need to introduce a whole bunch of child levels, you need to introduce either zero or one. And in the latter case, you can rename it away as Tucker demonstrated. Tucker's latest proposal does seem a bit simpler, and a bit easier to implement. And using incomplete types is a perfectly natural way to address this issue. Pascal, as our esteemed chair, please lock these folks in a room in Padua, and don't let them out until they agree upon a solution. ;-) ************************************************************* From: Dan Eilers Sent: Friday, January 31, 2003 12:51 PM Tucker wrote: > For what its worth, thanks to library unit renaming, I don't see > any need for clients to change with the "type C.T;" approach. yes, that is workable, but looks to me like a "dog". Ideally, one class in other languages should be implementable with one Ada package. Two is a stretch, and the third (library renaming) exceeds my threshhold of "dog". I'd like to know if we really have a requirement to solve the circular types problem for non-private types? If we can limit ourselves to private types, and if we also intend to solve the "private is separate" problem (which I hope we do), then I think one simpler solution solves both problems (just like circular types can be defined within a single package using private types instead of incomplete types). You would have some syntax, such as "private is separate" to say the private part is separately compiled. A normal "with" clause would continue to cause a semantic dependence on both the public part and the separately compiled private part. A different kind of "with" clause, such as "with p'abstract", would semantically depend on only the public part. This is what would be used at the top of the separately compiled private parts to avoid'circular semantic dependencies. This reduces the proposed 3-compilation-unit solution down to just one compilation unit with a separately compiled private part. ************************************************************* From: Tucker Taft Sent: Friday, January 31, 2003 8:28 PM The problem is that the visible parts depend on one another, not just the private parts. They each have visible operations that mention the other in formal parameters, presumably via an access type. This problem isn't solved by deferring the private part, as far as I can see. ************************************************************* From: Tucker Taft Sent: Friday, January 31, 2003 9:04 PM Robert A Duff wrote: > ... > Tucker doesn't like that proposal, for aesthetic reasons he has been > unable to articulate to *my* satisfaction. Yes I have aesthetic concerns, but I realize those are not easily conveyed to others. Basically, we have a totally new construct that looks like nothing that appears anywhere else in the language, and somewhat tortures the meaning of "separate." But my *non* "aesthetic" concerns (over and above the two concerns expressed by SIGAda 2002 attendees) are hopefully easier for others to understand, if not agree with: - it ends up creating two (or more) fully expanded names for the same type, with new, tricky-to-define-and-implement rules for when the names are equivalent; - the recommended idiom of use seems to indicate that type stubs really belong in a "prefix" or "abstract" (or parent) part of the unit, not in some randomly named unit; - the completing unit doesn't see the type stub, nor even know it exists, so it doesn't "know" it needs to match an access type representation that will be used by units that see only a stub. [To address this third concern, Randy has suggested requiring that the completing package must "with" (all) the package(s) containing the stub(s). This seems difficult to enforce, since you seem to have recreated the cyclic recompilation problem we were trying to eliminate. I.e., when you add a new stub somewhere, you have to go and check that the package referenced by the stub has a "with" clause on it, but there is no requirement that the package even exist, and in any case, it might be changed later. So when you (re)compile the completing package, you must scan all units in the system and see whether they now contain a stub. This seems to be creating a mutual compilation dependence between a unit containing a stub and the completing package, which sounds painful.] > ... > I'm also perfectly > willing to go along with Tucker's recent "type C.T;" proposal.... > > Tucker's latest proposal does seem a bit simpler, and a bit easier to > implement. And using incomplete types is a perfectly natural way to > address this issue. Glad to hear that even if my aesthetic concerns about type stubs are unconvincing, that the "type C.T" alternative isn't dead on arrival as far as you are concerned... > Pascal, as our esteemed chair, please lock these folks in a room in > Padua, and don't let them out until they agree upon a solution. ;-) Sounds like fun. I'll bring the Chianti... ;-) ;-) ************************************************************* From: Randy Brukardt Sent: Friday, January 31, 2003 10:17 PM > - it ends up creating two (or more) fully expanded names > for the same type, with new, tricky-to-define-and-implement > rules for when the names are equivalent; The number of names is meaningless; it's how many types there are that matters. The point about the complexity of matching the types is true, but I think it's a self-inflicted problem. ... > [To address this third concern, Randy has > suggested requiring that the completing package > must "with" (all) the package(s) containing the stub(s). This > seems difficult to enforce, since you seem to have recreated > the cyclic recompilation problem we were trying to eliminate. It's clearly a post-compilation check. That is, it's not enforced until the partition is created. It doesn't have to be anything else, I think. (After all, the existence of a child with the right name is essentially a post compilation check, at least from the perspective of the stub.) > I.e., when you add a new stub somewhere, you have to go and > check that the package referenced by the stub has a "with" > clause on it, but there is no requirement that the package > even exist, and in any case, it might be changed later. > So when you (re)compile the completing package, you must scan all units > in the system and see whether they now contain a stub. > This seems to be creating a mutual compilation dependence between > a unit containing a stub and the completing package, > which sounds painful.] That's definitely not the model I had. My concern is simply that there be no views of the completion that can't see (all) of the stubs for that completion. (That's the main gain of your proposal, I think.) ... > > Pascal, as our esteemed chair, please lock these folks in a room in > > Padua, and don't let them out until they agree upon a solution. ;-) > > Sounds like fun. I'll bring the Chianti... ;-) ;-) Better bring a lot... :-) :-) :-) ************************************************************* From: Robert A. Duff Sent: Saturday, February 1, 2003 9:08 PM No, not "dead on arrival" at all. It is, perhaps, the simplest solution proposed so far. All I ask is that the Padua meeting come up with a clear ARG-approved vote for *something* to send along to WG9. Perhaps with a strong admonishment not to nitpick, along the lines of Randy's "We have to have a solution to mutually dependent types in the Amendment." In support of Tucker's proposal: All well-written Ada 95 programs have a (mostly empty) parent package lying around. The "type C.T" can always be put there, without damaging clients. In legacy Ada 83 programs, the parent is Standard, and user's can't edit that. But Tucker showed how to use renaming tricks to get around that, still without damaging clients. Either way, there's never a need to totally restructure the package hierarchy to introduce mutually-dependent types. ************************************************************* From: Tucker Taft Sent: Friday, January 31, 2003 9:06 AM One way to make progress might be to compare attempts at actual wording for these various proposals. (I figure we don't have a shortage of material for the AI "discussion" sections ;-). Here is an attempt to provide wording for the "incomplete child type" proposal. [I have ignored the "is tagged" part in the wording, because it is probably time for us to separate tagged incomplete types out into their own AI, as at this point they seem orthogonal to our debate over the solution to the mutually dependent packages problem.] --------- proposed wording ------- Change paragraphs 3.10.1(2,3) as follows ("{}" are additions, "[]" are deletions, except in the syntax part): incomplete_type_declaration ::= TYPE {[package_identifier.]}defining_identifier[discriminant_part] {[is tagged]}; An incomplete_type_declaration requires a completion, which shall be a full_type_declaration {, a private_type_declaration, or a private_extension_declaration}. {If a package_identifier is present in the declaration, the completion shall occur immediately within the visible part of a package with this identifier, which shall be declared later and immediately within the innermost enclosing declarative region. Otherwise, if}[If] the incomplete_type_declaration occurs immediately within ... < rest remains the same >. Add the following sentences to the end of paragraph 3.10.1(11): If a package_identifier is present in the declaration, the incomplete type is declared immediately within the declarative region of the named package. Otherwise, the incomplete type is declared immediately within the innermost enclosing declarative region. Change 8.3(19) as follows: If the completion of a declaration is a declaration, then [within the scope of] {in places where} the completion {is visible}, the first declaration [is] {, and any declarations it completes, are} hidden from all visibility. Similarly, a discriminant_specification or parameter_specification is hidden [within the scope of] {in places where} a corresponding discriminant_specification or parameter_specification of a corresponding completion, or of a corresponding accept_statement {is visible}. --------- end of wording ---------- This last part deserves some discussion. The change from "within the scope" to "places where visible" seems necessary for any of our proposals, to avoid the "ripple" effect where an indirect "with" dependence can have significant effects on a unit outside the scope of the "with" clause. The original "within scope" wording would pull completions into all semantic dependents of the completing package, since the scope of a library item extends to all its dependents, including those units that do not have "with" visibility on the _item. But we have agreed in past discussions that we want the completing package (or a rename thereof) to be visible, not just somewhere within scope, if we are going to consider the type "completed." The new wording of the paragraph is intended to have no effect on existing code. One note about the syntax, to address Randy's concern. Adding the optional "[package_identifer.]" to the syntax could be treated by the parser roughly in the same way as the optional "[parent_unit_name.]" is treated in section 6.1 for defining_unit_name. You could allow this prefix on any type declaration, and then have a semantic rule that makes sure it is only used for incomplete type declarations. My belief is that this proposal will have the simplest wording and implementation effort, and with the use of the "Top" package approach I mentioned in an earlier message, can accommodate use of the feature for packages that are not part of a package hierarchy. ************************************************************* From: Robert Dewar Sent: Saturday, February 1, 2003 9:18 PM > In support of Tucker's proposal: All well-written Ada 95 programs have a > (mostly empty) parent package lying around. The "type C.T" can always > be put there, without damaging clients. In legacy Ada 83 programs, the > parent is Standard, and user's can't edit that. But Tucker showed how > to use renaming tricks to get around that, still without damaging > clients. Either way, there's never a need to totally restructure the > package hierarchy to introduce mutually-dependent types. I disagree with this claim. You can't just make a style statement like that without some real evidence of consensus. I see none. OK, so Bob Duff says a) almost all existing Ada 95 programs are badly written b) we don't care about programs that are badly written c) our proposal is only for well written programs This is not the way to get a proposal approved. I very much doubt that WG9 can be persuaded by an argument that says: a) we have a solution to the mutual types problem b) it's not really right, and we are not happy with it c) but we have to have something, so you should approve it anyway. The concentration must remain on getting a solution that has real consensus technically at the ARG and grass roots level, and only then does it make sense to take something to WG9. ************************************************************* From: Randy Brukardt Sent: Saturday, February 1, 2003 9:36 PM Bob said: > In support of Tucker's proposal: All well-written Ada 95 > programs have a (mostly empty) parent package lying around. Oh-oh. Those are fighting words, 'cause they imply that every line of Ada 95 code that's I've ever written is not "well-written Ada 95". There is no such package in Claw, or CBuild, or AdaServe, or anything else I've written. That's due, in large part, to the fact that I care about the names of things for the use-adverse. The names in Claw are too long the way it is, adding another level just to meet someone's idea of good style is ridiculous. Saying that you can use "renaming tricks" is fine, but my style is to use those only for compatibility with legacy code. In new code, I always use the full name. Case in point, I always use Ada.Text_IO, never Text_IO. An extra level of packages means that the names are even harder to read and write. So I use them only when it is necessary to the design. Having the language enforce someone's idea of good style (especially one not shared by many of us!) is a very bad idea. ************************************************************* From: Robert A. Duff Sent: Saturday, February 1, 2003 10:01 PM > > In support of Tucker's proposal: All well-written Ada 95 programs have a > > (mostly empty) parent package lying around. The "type C.T" can always > > be put there, without damaging clients. In legacy Ada 83 programs, the > > parent is Standard, and user's can't edit that. But Tucker showed how > > to use renaming tricks to get around that, still without damaging > > clients. Either way, there's never a need to totally restructure the > > package hierarchy to introduce mutually-dependent types. > > I disagree with this claim. OK, let me state it differently: There are two cases: you have a parent lying around (that's the style I usually like) or you don't. If you do, there's a clear place to put the incomplete type (in the parent of wherever the type already is). If you don't, the renaming tricks work seemlessly. Either way, there's no problem for clients. >...You can't just make a style statement like > that without some real evidence of consensus. I see none. OK, so Bob > Duff says > > a) almost all existing Ada 95 programs are badly written OK, I back off on that. > b) we don't care about programs that are badly written I never said that. I explicitly pointed out that those programs can easily deal with it. (I admit "badly written" was a poorly chosen value judgement.) My point was that there's no problem for those programs that choose that flat-namespace style. > c) our proposal is only for well written programs Not at all. Please read my comment again: you'll see that the proposal works fine for both styles, which is the goal. Note my claim, "Either way, there's never a need to totally restructure the package hierarchy to introduce mutually-dependent types." ************************************************************* From: Robert A. Duff Sent: Saturday, February 1, 2003 10:12 PM Randy said: > Bob said: > > > In support of Tucker's proposal: All well-written Ada 95 > > programs have a (mostly empty) parent package lying around. > > Oh-oh. Those are fighting words, I didn't *mean* them to be "fighting words". ;-) >... 'cause they imply that every line of Ada 95 > code that's I've ever written is not "well-written Ada 95". There is no such > package in Claw, or CBuild, or AdaServe, or anything else I've written. Heh? I thought everthing was under a parent CLAW or CBuild or whatever. The point is that if you happen to have a parent already, that's a reasonable place for the incomplete type decl. No big deal. If you don't, it's *still* no big deal because of package renamings. ... > Having the language enforce someone's idea of good style (especially one not > shared by many of us!) is a very bad idea. I completely agree -- no enforcing of style is intended here. ************************************************************* From: Randy Brukardt Sent: Saturday, February 1, 2003 9:56 PM I just looked at the implementation implications of Tucker's "type C.T" proposal in Janus/Ada. Janus/Ada is a syntax-directed compiler in the classic style (as it started as an class project, and the textbook was the dragon book). That means a syntax change (as opposed to addition) can have ripple effects throughout the compiler. The syntax that the compiler uses for incomplete types is currently: Type_Head discrim_option SEMICOLON where Type_Head is TYPE IDENTIFIER In order to implement Tucker's syntax, we'd have to change Type_Head to: TYPE IDENTIFIER DOT IDENTIFIER | TYPE IDENTIFIER Type_Head is used in all type declarations, including generic formal types. (There is a comment which says that the grammar is not LALR(1) if that is not done). The processing done for Type_Head is to enter an empty, dummy type into the symbol table. Discrim_Option "knows" where this is, and puts the discriminants into that type. This means that this change would have two major impacts on the compiler: All types (including formal types) would have to be able to do declarations into these "ghost" packages. An error would be generated, of course, but we can't stop processing the program for such a trivial appearing error. And we have to do something sensible (not crash!) in any case, even though the program is illegal. A large part of the bugs that we have to fix are errors in error handling. This is on top of the semantic check itself. The declarations of discriminants would have to be rewritten to support insertion into a ghost package rather than the current declaration location. It might be possible to "fool" this code into writing into a ghost package, but then that would have to be undone at every possible type declaration. In summary, this looks like a bigger change than type stubs. Indeed, if my proposed simplification to the completion rules was adopted, I don't believe that any change to type matching or to type declarations or use would be needed. The only real change (besides actually implementing the declaration) would be to context clause processing to "hook" the stub to the completion (which is necessary in Tucker's proposal as well as original type stubs). BTW, I mentioned yesterday that a post-compilation rule was necessary to make that idea work. That's not actually true (although it might still be a good idea to have one). The only impact if the completion did not with the package containing the stub would be that the completion would never match the stub (no matter what was withed elsewhere). That seems tolerable to me, but since it would probably cause mysterious errors, a post-compilation check is better. Note that the post-compilation check would have to not be triggered if there was no completion at all. I'll try to write up the wording for this, but probably not until I'm on my way to the meeting. ************************************************************* From: Tucker Taft Sent: Monday, February 3, 2003 1:55 PM Randy Brukardt wrote: > > I just looked at the implementation implications of Tucker's "type C.T" > proposal in Janus/Ada. > > Janus/Ada is a syntax-directed compiler in the classic style (as it started > as an class project, and the textbook was the dragon book). That means a > syntax change (as opposed to addition) can have ripple effects throughout > the compiler. I sympathize here. Our compiler does essentially all semantic analysis on the fly while parsing. We don't build up syntax trees for anything much bigger than an expression, and we do the first "pass" of overload resolution while building up expression trees. > The syntax that the compiler uses for incomplete types is currently: > Type_Head discrim_option SEMICOLON > where Type_Head is > TYPE IDENTIFIER > > In order to implement Tucker's syntax, we'd have to change Type_Head to: > TYPE IDENTIFIER DOT IDENTIFIER | > TYPE IDENTIFIER > > Type_Head is used in all type declarations, including generic formal types. > (There is a comment which says that the grammar is not LALR(1) if that is > not done). I presume that is not the only place where the grammar is not LALR(1)! > The processing done for Type_Head is to enter an empty, dummy type into the > symbol table. Discrim_Option "knows" where this is, and puts the > discriminants into that type. > > This means that this change would have two major impacts on the compiler: > All types (including formal types) would have to be able to do > declarations into these "ghost" packages. An error would be generated, of > course, but we can't stop processing the program for such a trivial > appearing error. And we have to do something sensible (not crash!) in any > case, even though the program is illegal. A large part of the bugs that we > have to fix are errors in error handling. This is on top of the semantic > check itself. > The declarations of discriminants would have to be rewritten to support > insertion into a ghost package rather than the current declaration location. > It might be possible to "fool" this code into writing into a ghost package, > but then that would have to be undone at every possible type declaration. Would you really need to undo anything? It seems that you would simply allow any sort of type in a "ghost" package, but of course give some kind of error message if it turns out not to be incomplete, because that is the "arbitrary" restriction that the silly ARG imposed. You would then also be ready for future enhancements to the language ;-). > In summary, this looks like a bigger change than type stubs. It is of course impossible for anyone outside RR to evaluate that, but I can say that for our compiler, dealing with the "availability" rules associated with type stubs was looking like a brand new kettle of fish. > ... > BTW, I mentioned yesterday that a post-compilation rule was necessary to > make that idea work. That's not actually true (although it might still be a > good idea to have one). The only impact if the completion did not with the > package containing the stub would be that the completion would never match > the stub (no matter what was withed elsewhere). That seems tolerable to me, > but since it would probably cause mysterious errors, a post-compilation > check is better. Note that the post-compilation check would have to not be > triggered if there was no completion at all. It would seem that if you go this far, then you should only allow one stub in the program. Allowing multiple stubs seems like gratuitous complexity in the matching rules. Was it your intent to allow only one stub, and require that the stub be visible (due to "withing" the enclosing package or a renaming thereof) at the point of the completing type declaration? > I'll try to write up the wording for this, but probably not until I'm on my > way to the meeting. ************************************************************** From: Pascal Leroy Sent: Monday, February 3, 2003 4:20 AM > There seems to be some feeling that type stubs > were almost right, and we should either leave > them as is, or add some kind of special context > clause such as "separate with ..." and be done with it. I am one of the people who thought that type stubs were "almost right" and I must admit that I am getting more and more uncomfortable with this proposal. I think that your "philosophizing" nicely summarizes the issues, and I only wished you wrote it earlier (it's only a few days before the meeting...). > 1) Type stubs look and feel like an add-on to the language. Agreed, but then so do your latest proposals regarding package prefixes and "type C.T". Other than shuffling the keywords around, I don't see that they integrate much better in the existing language. Actually, other that package abstracts, all the proposals look like warts. > 2) Type stubs are not necessarily "seen" by the type completer. You're right, this might be a serious implementation problem, although not necessarily more complicated than having three-part types or a new kind of compilation unit. > 3) Type stubs seems to require a particular "idiom" of use > that makes it seem clear that some kind of package "prefix" > or "abstract" is what we *really* wanted but couldn't somehow > bring ourselves to provide. Yes, it's always suspicious when we have to recommend a particular idiom to achieve some effect that should be straightforwardly supported by the language. See for instance the idioms to "simulate" multiple inheritance or downward closures in Ada 95. > 4) Type stubs have the unfortunate situation where the > same type has two different fully expanded names. I cannot get too excited about this problem. At the implementation level you have to match up distinct type declarations, whatever the model, so it doesn't bother me too much that they have distinct expanded names. > b) package prefix/abstract feels like a big change, requiring a new > kind of program unit, a new kind of context clause, and non > obvious rules about what part of the parent unit a child > prefix/abstract can see. Admittedly, the package prefix is > probably the most powerful and flexible. I think that package abstract is in some sense the most logical choice, and that it's the approach we would choose if the year were 1980 and we were not concerned about disrupting implementations. > c) "type C.T;" seems like a natural outgrowth of two basic facts: > i) the incomplete type declaration is the fundamental way > of supporting mutually dependent types in Ada, but it > is limited to only working within a single package spec; > ii) child packages are the fundamental way of splitting a large > abstraction into smaller, more manageable parts, but they are > limited in that mutually dependent types cannot cross > these pieces of the abstraction. I am sorry, I don't see the connection. Both (i) and (ii) are true statements, but I don't understand why you absolutely want to tie a solution to the problem of mutually-dependent types to child units. To me this fails your criteria #1 above, and using child units for that purpose looks totally awkward. > The major objection to > the more flexible incomplete type declaration seems to be that > it might not just "drop" into existing code. As far as I am concerned there is also an aesthetical reason, which I find hard to convey, but I feel strongly about it: child units are not the proper construct to solve the problem at hand. ************************************************************* From: Tucker Taft Sent: Monday, February 3, 2003 2:35 PM Pascal Leroy wrote: > ... > > c) "type C.T;" seems like a natural outgrowth of two basic facts: > > i) the incomplete type declaration is the fundamental way > > of supporting mutually dependent types in Ada, but it > > is limited to only working within a single package spec; > > ii) child packages are the fundamental way of splitting a large > > abstraction into smaller, more manageable parts, but they are > > limited in that mutually dependent types cannot cross > > these pieces of the abstraction. > > I am sorry, I don't see the connection. Both (i) and (ii) are true statements, > but I don't understand why you absolutely want to tie a solution to the problem > of mutually-dependent types to child units. To me this fails your criteria #1 > above, and using child units for that purpose looks totally awkward. In my personal view of the world, if two types are mutually dependent, they are in some sense part of the "same" abstraction, or at least parts of two closely related abstractions. And in this same world view, all parts of a single abstraction should be grouped together within a single part of the program's namespace. Ada 95 pushes the programmer this way since generally parts of the same abstraction all need visibility on some common bit of hidden information, such as a private part of some package. We considered the (dreaded) "with private" approach to "opening up the Kimono" of a private type, and rejected it in the Ada 9X process. We concluded that there were many advantages to keeping visibility on hidden information within a well defined part of the namespace. This is the reason I see support for mutual dependence only between packages in the same package hierarchy as a perfectly acceptable limitation. Combined with the ability to hide this package hierarchy using renaming, in the situations where it is undesirable to tie these packages together in the namespace, it seemed like a good, simple (even elegant? ;-) solution to the problem. > > The major objection to > > the more flexible incomplete type declaration seems to be that > > it might not just "drop" into existing code. > > As far as I am concerned there is also an aesthetical reason, which I find hard > to convey, but I feel strongly about it: child units are not the proper > construct to solve the problem at hand. Unfortunately, design issues often depend on our intuitions about what makes sense and what doesn't. Sometimes our intuitions line up, and sometimes they don't. With this one, we aren't doing a lot of lining up ;-(. I think we also are being reminded why design by committee is so hard, if the entire committee is committed to producing an excellent, intuitively obvious, elegant, aesthetic, implementable design. ************************************************************** From: Tucker Taft Sent: Monday, February 3, 2003 8:16 PM Tucker replied to me: > > Type_Head is used in all type declarations, including generic formal types. > > (There is a comment which says that the grammar is not LALR(1) if that is > > not done). > > I presume that is not the only place where the grammar is not LALR(1)! Our grammar is LALR(1) of course; the comment just was there to tell me not to bother trying to separate these cases, because it won't work. I know that otherwise, I'd try to "improve" the grammar to eliminate these useless cases, and waste a lot of time. There is no obvious relationship between our compiler's grammar and the one in the RM. (For the obvious reasons). > > The declarations of discriminants would have to be rewritten to support > > insertion into a ghost package rather than the current declaration location. > > It might be possible to "fool" this code into writing into a ghost package, > > but then that would have to be undone at every possible type declaration. > > Would you really need to undo anything? It seems that you would simply allow > any sort of type in a "ghost" package, but of course give some kind > of error message if it turns out not to be incomplete, because that is > the "arbitrary" restriction that the silly ARG imposed. You would > then also be ready for future enhancements to the language ;-). You'd have to "undo" declaring into the ghost package, because if you didn't, everything declared after it would go into the ghost. That would be bad, even for an illegal program, because it would cause cascading errors. And that would have to be done in every kind of type declaration. > > In summary, this looks like a bigger change than type stubs. > > It is of course impossible for anyone outside RR to evaluate that, > but I can say that for our compiler, dealing with the "availability" > rules associated with type stubs was looking like a brand new > kettle of fish. With the simplified rules you and I have been discussing, I believe that type stubs (not including tagged incomplete) would take 4 hours of work. Period. One mildly messy routine to run at the end of context clause processing (to look for completions of any stubs loaded), and one rather simple post-compilation check. Nothing for "availablility" at all, it follows from standard type matching and visibility. It'll take longer to figure out where the routines go than to write and test them. > > ... > > BTW, I mentioned yesterday that a post-compilation rule was necessary to > > make that idea work. That's not actually true (although it might still be a > > good idea to have one). The only impact if the completion did not with the > > package containing the stub would be that the completion would never match > > the stub (no matter what was withed elsewhere). That seems tolerable to me, > > but since it would probably cause mysterious errors, a post-compilation > > check is better. Note that the post-compilation check would have to not be > > triggered if there was no completion at all. > > It would seem that if you go this far, then you should only allow one stub > in the program. Allowing multiple stubs seems like gratuitous complexity > in the matching rules. Was it your intent to allow only one stub, > and require that the stub be visible (due to "withing" the enclosing > package or a renaming thereof) at the point of the completing > type declaration? Not on Saturday. Thinking about that yesterday and this morning, I concluded that it would be better to restrict to one stub per type (otherwise, you have the stub-stub matching rules, or they don't match, which is weird). It isn't critical, but it would avoid surprises. Anyway, this idea may be OBE. Since I thought that your proposal was too much work to implement, Pascal decided to propose one that would be ten times more work. :-) ************************************************************** ************************************************************** ************************************************************** **************************************************************