!standard 4.1.3(12) 09-05-27 AI05-0135-1/05 !standard 7.1(2) !standard 7.1.3(7) !standard 8.4(5/2) !standard 8.4(11) !standard 8.4(18) !standard 8.5.3(3) !standard 8.5.3(4) !standard 12.3(2/2) !standard 12.3(18) !class Amendment 09-01-31 !status work item 09-01-31 !status received 08-12-17 !priority Medium !difficulty Medium !subject "Integrated" nested packages !summary Add "use package P is ..." to allow integrated packages and package renames. !problem One common idiom is to write a derived type to take a type declared within an instantiation (or some other unit) and bring it out to the same level as the instantiation (or into the new unit): package T_Vecs is new Vectors(Element => T); type T_Vec is new T_Vecs.Container with null record; This is often done to bring the type and its operations to this location. But often a new, distinct type is not what is wanted. Moreover, some related entities (classwide operations, exceptions, generic operations) don't get exported this way, so additional renaming or declarations are required to completely make the instance transparent. This use of a derived type is really something of a kludge. What is really wanted here is a way to make all of the declarations declared in the instantiation visible as if they were declared at the same level as the instantiation (as opposed to within it). Consider a package which needs to export both a private type and an instance of one of the container generics for that type. Currently, this requires something like package Pkg is package Inner is type T is private; private ... end Inner; package I is new Some_Container_Generic (Inner.T); end Pkg; What is wanted here is a way to make all of the declarations visible in Pkg.Inner (and perhaps Pkg.I; that's less clear) visible as if they were declared immediately within Pkg. This special case of the problem alone has been viewed as being sufficiently important to merit considering major changes in the language (see the 4 alternative versions of AI05-0074). An unwanted nested package may also be introduced when a user wishes to declare a non-primitive operation for a tagged type, as in package Pkg is type T is tagged ... ; package Inner is function Not_Primitive (X : T) return T; end Inner; end Pkg; As in the previous example, what is wanted here is a way to make all of the declarations visible in Pkg.Inner visible as if they were declared immediately within Pkg. !proposal A package or package rename which is declared immediately within the visible part of an enclosing package may be declared with the optional reserved word "use". This indicates that the package is to be "integrated" into its parent; this means that clients of the parent have the ability to name the constructs declared within the integrated package as though they were declared within the enclosing package. One scenario where this construct may be useful is in declaring a package that exports both a private type and a container generic instance for that type without requiring that clients deal with nested packages. This can be accomplished using this new construct as follows: with Some_Container_Generic; package Pkg is use package Inner is type T is private; private ...; end Inner; use package I is new Some_Container_Generic (T); -- exports type Valise end Pkg; with Pkg; package Client is X : Pkg.T; Y : Pkg.Valise; end Client; !wording Modify 4.1.3(12) as follows The selector_name shall resolve to denote a declaration that occurs immediately within the declarative region of the package or enclosing construct (the declaration shall be visible at the place of the expanded name — see 8.3) {, or to denote a declaration that is use-visible by selection at the point of the selector_name (see 8.4)}. The expanded name denotes that declaration. Modify 7.1(2) to allow optional "use" syntax: package_declaration ::= {use} package_specification; Add after 7.1.3(7) (i.e. append to the end of the Static Semantics section): If the optional reserved word "use" is present in a package_declaration, then the package is said to be "integrated" (see 8.4). Add after 8.4(5/2) (i.e. append to the end of the Legality Rules section) The declaration of an integrated package or package renaming shall occur immediately within the visible part of an enclosing package or generic package. An integrated package or package renaming shall not be library unit (see 10.1.1). The limited view of an integrated package is not integrated. Add after 8.4(11) (i.e. append to the end of the Static Semantics section) At any point where an integrated package or package renaming is either potentially use-visible or directly visible, and where an entity declared immediately within the package or renamed package is visible, the entity is potentially use-visible. At the point of an expanded_name whose prefix denotes a package P (or a rename thereof) which immediately encloses the visible declaration of an integrated package or package renaming, any visible declaration declared immediately within the integrated package (or, in the case of an integrated package renaming, within the renamed package) is said to be "potentially use-visible by selection" at the point of the selector_name. In addition, if the declaration of an integrated package or package rename is "potentially use-visible by selection" at the point of a selector_name, then so are any visible declarations declared immediately within the package or renamed package. A declaration which is "potentially use-visible by selection" at the point of a selector_name is said to be "use-visible by selection" at that point unless - the defining name of the declaration is not the same as the selector_name or - a visible homograph of the declaration is declared in P or - more than one such declaration is potentially use-visible by selection at the point of the selector_name and at least one of them is not overloadable. Add after 8.4(18) (in the Examples section) generic type T is private; package G is function Make return T; end G; with G; pragma Elaborate (G); package Pkg1 is type T is null record; use package I is new G (T); end Pkg1; with Pkg1; package Pkg2 is X : Pkg1.T := Pkg1.Make; use Pkg1; Y : T := Make; end Pkg2; Modify 8.5.3(3) to allow optional "use" syntax: package_renaming_declaration ::= {use} package defining_identifier renames ... Add after 8.5.3(4) (i.e. append to the end of the Static Semantics section): If the optional reserved word "use" is present in a package_renaming_declaration, then the package renaming is said to be "integrated" (see 8.4). Modify 12.3(2/2) to allow optional "use" syntax: generic_instantiation ::= {use} package defining_program_unit_name is ... Add after 12.3(18) (i.e. append to the end of the Static Semantics section): If the optional reserved word "use" is present in a generic_instantiation, then the package instance is said to be "integrated" (see 8.4). !discussion Just to summarize, adding the reserved word "use" to a package or package rename declaration has three main effects: 1) It is as though an implicit use_clause were added immediately after the package declaration, so that visible declarations in the integrated package's spec become potentially use-visible within the enclosing package. This allows: package P is use package Q is X : Integer := 0; end Q; -- use Q; Y : Integer := X; end P; 2) It is as though an implicit use_clause were added immediately after any use_clause that applies to the enclosing package. This allows: package P is use Package Q is X : Integer := 0; end Q; end P; with P; use P; -- use P.Q; package R is Y : Integer := X; end R; 3) It is as though an implicit use_clause were in effect when resolving the selector_name of a selected name whose prefix denotes the enclosing package. This allows: package P is use Package Q is X : Integer := 0; end Q; end P; with P; package R is Y : Integer := P. -- use P.Q X; end R; Note that this construct is not defined in terms of implicit use_clauses. This is just an alternative way to view it for expository purposes. ---- #2 above states It is as though an implicit use_clause were added immediately after any use_clause that applies to the enclosing package. Note that this informal description applies recursively to the implicit withs described in #1 - #3. This allows: use System; package P is use Package S_Ren renames System; end P; with P; package Q is use package P_Ren renames P; end Q; with Q; use Q; package R is A : Address; end R; ---- The usual use_clause name resolution rules apply. In the case of the following example package P is use Package Q is X, Y : Integer := 0; end Q; X : Float; end P; , this means that the use adding/deleting the reserved word "use" in the declaration of package Q can only make a difference to clients when resolving occurrences of the identifier "Y", not "X". ---- If it is decided that integrated package renames are not wanted, it would be easy to remove them from the proposal. As Erhard has pointed out, there are good reasons to consider this option. It is certainly possible to use integrated package renames to write code that is very difficult to read; this was already true of Ada83 use clauses, but integrated package renames seem to offer substantially greater potential for confusion. Integrated package renames also make possible a number of odd corner cases: limited with Foo; package P is use package Bar renames Foo; end P; package Outer is package Inner is use package Outer_Rename renames Outer; end Inner; end Outer; Finally, integrated package renames are not needed to solve any of the problems that provided the original motivation for introducing integrated packages. The wording changes need to delete integrated package renames from this AI would be straightforward. This would include: - eliminating the wording changes in 8.5.3 - replacing "integrated package or package renaming" with "integrated package" throughout - eliminating references to integrated package renamings in the change appended after 8.4(11) ---- Tuck suggests that the name of an integrated package (whether a rename or not) should not itself become "use"-visible when the enclosing package is "use"d. Consider the following example (supplied by Tuck): Text_IO, Sequential_IO, and Direct_IO each include renames of each exception declared in the package IO_Exceptions. It is a no-brainer to replace those renames with a: "use package renames Ada.IO_Exceptions;" in Text_IO, Sequential_IO, and Direct_IO. However, what should the be? Well, most natural would be: "use package IO_Exceptions renames Ada.IO_Exceptions;" but now suppose you have: with Ada.IO_Exceptions; with Ada.Text_IO; use Ada; procedure What_Fun is use Text_IO; -- oops, IO_Exceptions suddenly disappears begin ... exception when IO_Exceptions.Name_Error => -- oops, this doesn't compile any more! end What_Fun; If it is decided that this is what we want, then corresponding wording would probably be fairly straightforward (as long as we don't also require elegance, in which case the problem may be overconstrained). As an aside, note that the above example would be allowed if "IO_Exceptions.Name_Error" were replaced with "Name_Error". Tuck observes that this rule should "apply to *all* integrated packages, not just integrated package renames, since the same problem comes up when you declare an integrated subpackage, or an integrated generic instance. You really don't want the names of the integrated packages cluttering up the namespace when you do a "use" of the enclosing package, but you *do* want them when trying to disambiguate where you would be using a complete expanded name." If there is general agreement on this point, then the two alternatives described below should be discarded because they only address the case of an integrated package rename. Another solution would be to allow anonymous integrated package renames, as in use package <> renames Ada.Io_Exceptions; This would be equivalent to use package Some_Identifier_That_Occurs_Nowhere_Else_In_The_Program renames Ada.Io_Exceptions; Anonymous non-renames seem like a bad idea, but perhaps anonymous integrated renames would be ok? A third approach to this problem would be to add a name resolution preference rule: if a package rename has the same simple name as the renamed entity (which might or might not itself be a rename), then prefer the renamed (and similarly through chains of renames as long as the simple name remains unchanged). This would not introduce the sort of Beaujolais effects that are typically associated with preference rules, but it does seem that treating a simple-name-preserving rename differently than a simple-name-nonpreserving rename might have some peculiar consequences (e.g., there might be some odd interactions with formal packages). This is probably a bad idea. ---- Consider the following example: package Pkg is type T is ... ; use package Inner is function Is_Brobdingnagian (X : T) return Boolean; end Inner; end Pkg; with Pkg; package Client is X : Pkg.T := ... ; Y : Boolean := Pkg.Is_Brobdingnagian (X); -- ok type D is new Pkg.T; Dx : D := ... ; Dy : Boolean := Is_Brobdingnagian (Dx); -- illegal end Client; Is this a problem? The function looks a lot like a primitive operation of the type, but the fact that it isn't really one does show through in some cases. ---- A limited view of a non-instance non-rename integrated package declaration *could* be defined to be integrated, thus allowing package P is use package Q is type T is new Integer; end Q; end P; limited with P; package R is type Ref is access P.T; -- currently illegal end R; Would this be desirable? It won't work for instances or package renames. ---- Note that when resolving the name P.Q, where P denotes a package, declarations with names other than "Q" may be potentially use-visible by selection at the point of the selector_name, but they will never be use-visible by selection at that point. This is necessary because a visible integrated package declared directly in P must be potentially use-visible by selection regardless of the name of the package. The following example is therefore legal: package P is use package P1 is Aaa : Integer; use package Inner is function F return Integer; end Inner; end P1; use package P2 is use package Inner is Bbb : Integer; function F return Float; end Inner; end P2; end P; with P; package Q is X : Float := P.F; end Q; At the point of the selector_name, 8 declarations are potentially use-visible by selection: P.P1, P.P1.Inner, P.P1.Inner.F, P.P2, P.P2.Inner, P.P2.Inner.F, P.P1.Aaa, and P.P2.Bbb but only 2 declarations are use-visible by selection: P.P1.Inner.F and P.P2.Inner.F Note that this means that the word "such" in "more than one such declaration is potentially use-visible" is important. In this example, package P is use package Q is X, Y : Integer; end Q; end P; with P; package R is Z : Integer renames P.X; end R; both X and Y are potentially use-visible at the point of the selector_name, but the word "such" implies that only declarations named "X" are to be considered. Thus, the example is legal. --!corrigendum 8.4(3) !ACATS test Add ACATS tests for this new feature. !appendix From: Steve Baird Date: Wednesday, December 17, 2008 3:30 PM Tucker Taft wrote in another thread: > One thing I think is important is being > able to take a type declared within an instantiation > and bring it out to the same level as the instantiation: > > package T_Vecs is new private Vectors(Element => T, others => private); > type T_Vec is new T_Vecs.Container with null record; > This is one instance of a general problem that comes up in other contexts as well. We are used to having to introduce a derived type, but this "idiom" is really a bit of a hack if the only reason for doing this (as opposed to declaring a subtype) is to avoid having to rename all of the primitive operations. The derived type is, of course, necessary if we are completing a private type (completing a private type with a subtype was discussed and discarded a long time ago). Within AdaCore there has been some discussion of a generalized renaming construct of some sort. No consensus regarding syntax has been reached (and it is not because so many good alternatives have been proposed that the choice is difficult). For purposes of discussion, I'll go with what Bob Duff describes as a "string of existing reserved words that more-or-less means the right thing": declare renames for .all; This construct would be defined to be equivalent to a sequence of rename and (constraintless) subtype declarations which rename every visible entity exported from the given package (as seen from the point of the rename declarations). The simple names of the renames would match those of the renamed entities. This would allow with G; package Pkg1 is package I is new G ( ... ); -- exports a type I.T; declare renames for I.all; end Pkg1; with Pkg1; package Pkg2 is X : Pkg1.T; end Pkg2; Typically this construct would occur in a package spec. Thus, the focus of this construct is very different than that of a use clause. A use clause is associated with importing whereas this construct is intended primarily for (re)exporting. The effects of this construct within the scope in which it occurs would be similar in some ways to that of a use clause, but only because that is what happens to fall out - that is not an essential part of the design. If it seemed desirable to emphasize this similarity, one could imagine syntax that somehow uses the reserved word "use". Syntax along the lines of package <> renames Some_Package; might emphasize that this is somewhat like declaring a package rename and than "inlining" it into the enclosing scope. Alternatively, it could be argued that this syntax makes no sense at all. There has also been some discussion of a finer-granularity rename-with-all-the-trimmings construct for a type rather than a package (somewhat like the relationship between "use" and "use type"). Something like subtype S is Some_Package.T with renames; would implicitly declare renames not only of T, but also of all of its primitive operations which are visible at this point. This might be preferable to the package-rename construct because it doesn't implicitly introduce non-overloadable declarations. In a case like type T is ... ; type Cursor is ... ; package I is new G (...); -- exports a type I.Cursor declare renames for I.all; , the two Cursor declarations would collide. On the other hand, the definition of "directly visible at this point" might be somewhat more complicated in the case of a subtype as opposed to a package. Obviously the type and package rename proposals are orthogonal. Any of the four combinations (package only, type only, both, neither) could be defined. Nits: 1) A named number is "renamed" as Foo : constant := The_Package.Foo; 2) Parameter default values for subprograms are preserved as is done for derived subprogram parameters in the case where no substitution is needed. 3) A (desirable) consequence of defining this construct via an equivalence rule is that these implicitly declared renames are treated as though they were explicitly declared for purposes of overload resolution, overriding, etc. Does this idea seem worth pursuing? This construct is intended to address one of the deficiencies of the deferred-instance-body-elaboration proposal relative to the post-private-visible-part proposal by eliminating the need to declare a derived type (which introduces freezing problems in some cases with the former proposal). I do not want to gratuitously load the language down with new features. In my opinion, some version of this proposal coupled with the deferred-instance-body-elaboration proposal would be preferable to the post-private-visible-part proposal. Is this issue therefore tied to the "instantiating a generic with a private type" question, or is it an independent question? **************************************************************** From: Tucker Taft Date: Wednesday, December 17, 2008 4:13 PM I think the subtype-oriented construct is simpler to deal with (because it avoids the non-overloadables), and matches pretty closely what Dan Eilers and others have been suggesting for years. The "subtype S is P.T with renames;" actually seems pretty intuitive. Presumably it should work in combination with others, so that there is no complaint if multiple "renames" occur for the same operation, producing only one actual rename: subtype S1 is P.T1 with renames; subtype S2 is P.T2 with renames; This also addresses the complaints about "use type" which only makes operators visible: subtype Enum is P.Enum with renames; would make all of the enumeration literals for P.Enum directly visible. I suspect you are introducing a wicked Beaujolais effect, by the way... ;-) It is unclear whether we would want to solve that. Perhaps the solution is that if by doing this you end up with a non-overloadable being hidden by an overloadable, then both are hidden -- essentially the use-clause cancellation rules. **************************************************************** From: Steve Baird Date: Wednesday, December 17, 2008 5:57 PM ... > The "subtype S is P.T with renames;" actually seems pretty intuitive. > Presumably it should work in combination with others, so that there is > no complaint if multiple "renames" occur for the same operation, > producing only one actual rename: > > subtype S1 is P.T1 with renames; > subtype S2 is P.T2 with renames; > There go those simple "equivalence rule" semantics ... Somehow I'm not surprised. > This also addresses the complaints about "use type" which only makes > operators visible: > > subtype Enum is P.Enum with renames; > > would make all of the enumeration literals for P.Enum directly > visible. > > I suspect you are introducing a wicked Beaujolais effect, by the > way... ;-) You are talking about a situation where adding or deleting the "with renames" takes you from one legal interpretation to another? It's easy to see how the resolution of a name could change within the declarative region where the subtype declaration occurs. Outside of that region, I'm not so sure. > It is unclear whether we would want to solve that. Agreed. If you have an uplevel reference and then someone adds a hiding declaration to an intermediate scope, nobody is surprised when the resolution of the name changes. How is this any different than a situation like package Pkg is type T is null record; -- function F (X : T := (null record)) return Integer; end Pkg; package body Pkg is ... ; F : constant Integer := 17; begin declare type D is new Pkg.T; X : constant Integer := F; begin null; end; where uncommenting the declaration of the function F changes the resolution of the use of F in the declare block? > Perhaps the solution is that if by doing this > you end up with a non-overloadable being hidden by an overloadable, > then both are hidden -- essentially the use-clause cancellation rules. I'd like to see a plausible example where this rule would prevent something bad from happening. **************************************************************** From: Bob Duff Date: Wednesday, December 17, 2008 6:35 PM > Does this idea seem worth pursuing? I've wanted something like this for years. > I do not want to gratuitously load the language down with new features. > In my opinion, some version of this proposal coupled with the > deferred-instance-body-elaboration proposal would be preferable to the > post-private-visible-part proposal. I tend to agree. > Is this issue therefore tied to the "instantiating a generic with a > private type" question, or is it an independent question? Independent. **************************************************************** From: Bob Duff Date: Wednesday, December 17, 2008 6:39 PM > I think the subtype-oriented construct is simpler to deal with > (because it avoids the non-overloadables), ... In my opinion, the subtype-oriented one doesn't really solve the problem. I much prefer the package-oriented version. This issue is all about visibility, and has nothing to do with types. I want to reexport all the stuff in that package from this package. That includes exceptions, generics, etc -- not just operations (primitive ones?) of some type. I think it can be done without Beaujolais effects. If there's a name clash, all decls are hidden (or don't get renamed, or some such). **************************************************************** From: Bob Duff Date: Wednesday, December 17, 2008 6:52 PM > Agreed. If you have an uplevel reference and then someone adds a > hiding declaration to an intermediate scope, nobody is surprised when > the resolution of the name changes. Well, I won't claim to be surprised, but I am annoyed. I consider this to be a Beaujolais-like effect. In the case of subunits and child units, it's particularly bad, because it crosses source-file boundaries. My hobby language doesn't have that sort of implicit hiding, by the way. Ichbiah came up with a quite clever way of avoiding Beaujolais effects for use clauses. I'm a little surprised he didn't apply this idea elsewhere. >...How is > this any different than a situation like > > package Pkg is > type T is null record; > -- function F (X : T := (null record)) return Integer; > end Pkg; > > package body Pkg is ... ; > > F : constant Integer := 17; > begin > declare > type D is new Pkg.T; > X : constant Integer := F; > begin > null; > end; > > where uncommenting the declaration of the function F changes the > resolution of the use of F in the declare block? This case is even worse, because there's no F in sight to hide the outer one. It seems very bad for an implicit decl to hide an explicit one. This is exactly the same problem that Pascal's 'with' statement has been criticized for. When you say, "with SomeRecord do...", a component declared off in the far boondocks hides a local variable. I think for the above, if F is uncommented, _both_ F's should be hidden, making the program illegal, thus avoiding the Beaujolais-like effect. > Perhaps the solution is that if by doing this > > you end up with a non-overloadable being hidden by an overloadable, > > then both are hidden -- essentially the use-clause cancellation > > rules. > > > > I'd like to see a plausible example where this rule would prevent > something bad from happening. In my opinion, you have shown such an example above. ;-) Just replace type D with this new package-reexporting gizmo. By the way, why would you ever want to use this new gizmo in a nested block or procedure? Isn't essentially the same as a use clause in that case? **************************************************************** From: Randy Brukardt Date: Wednesday, December 17, 2008 7:47 PM > >...How is this any different than a situation like > > > > package Pkg is > > type T is null record; > > -- function F (X : T := (null record)) return Integer; > > end Pkg; > > > > package body Pkg is ... ; > > > > F : constant Integer := 17; > > begin > > declare > > type D is new Pkg.T; > > X : constant Integer := F; > > begin > > null; > > end; > > > > where uncommenting the declaration of the function F changes the > > resolution of the use of F in the declare block? > > This case is even worse, because there's no F in sight to hide the > outer one. > It seems very bad for an implicit decl to hide an explicit one. I agree. This case is a serious bug waiting to happen; it's unfortunate that we can't make this illegal. Surely we don't want any *more* cases like this. (This gets to the reason that I dislike derived types as much as I do: you can't figure out what is getting declared without a fancy tool. It's the reason I hate use clauses multiplied by 5.) > This is exactly the same problem that Pascal's 'with' > statement has been criticized for. When you say, "with SomeRecord > do...", a component declared off in the far boondocks hides a local > variable. > > I think for the above, if F is uncommented, _both_ F's should be > hidden, making the program illegal, thus avoiding the Beaujolais-like > effect. Right, although I suspect that the compatibility cost might be too severe to do it now. For instance, such a rule could very well make instantiating a container in a nested scope illegal, because of some outer object named "Element" or "First" or "Last". And I have a lot of objects with names like that... > > > Perhaps the solution is that if by doing this you end up with a > > > non-overloadable being hidden by an overloadable, then both are > > > hidden -- essentially the use-clause cancellation rules. > > > > > > > I'd like to see a plausible example where this rule would prevent > > something bad from happening. > > In my opinion, you have shown such an example above. ;-) Just replace > type D with this new package-reexporting gizmo. > > By the way, why would you ever want to use this new gizmo in a nested > block or procedure? Isn't essentially the same as a use clause in > that case? That's interesting: that suggests that this "gizmo" really *is* a use clause -- it's just one that is visible to clients (unlike the normal variety). I wonder if it would be better to think of it that way (we wouldn't need new visibility rules then). Something like: declare use Pkg; or (if we were willing to have a new keyword): export use Pkg; and only allow that in a package (or generic package) specification (or just have it have the same meaning as a usual use clause). After all, didn't someone say this is all about visibility? Maybe we should say so (renames are not necessarily about visibility; they're more about shorthands). **************************************************************** From: Steve Baird Date: Wednesday, December 17, 2008 8:07 PM > Right, although I suspect that the compatibility cost might be too > severe to do it now. For instance, such a rule could very well make > instantiating a container in a nested scope illegal, because of some > outer object named "Element" or "First" or "Last". And I have a lot of > objects with names like that... > I didn't follow this. You have declare Last : Integer; package Ii is new Gg; -- exports a function Ii.Last How does Ii become illegal? **************************************************************** From: Randy Brukardt Date: Wednesday, December 17, 2008 8:45 PM > > Right, although I suspect that the compatibility cost might be too > > severe to do it now. For instance, such a rule could very well make > > instantiating a container in a nested scope illegal, because of some > > outer object named "Element" or "First" or "Last". And I have a lot > > of objects with names like that... > > > > I didn't follow this. I don't follow it either, rereading it. :-) > You have > > declare > Last : Integer; > package Ii is new Gg; -- exports a function Ii.Last > > How does Ii become illegal? I was thinking of the derived from contents case, but I botched the explanation badly. declare Last : Integer; package Ii is new Gg; -- exports a function Ii.Last type My_Vector is new Ii.Vector with null record; This sort of thing is pretty common. (OTOH, I only do it in package specifications.) **************************************************************** From: Steve Baird Date: Wednesday, December 17, 2008 7:52 PM > I think for the above, if F is uncommented, _both_ F's should be > hidden, making the program illegal, thus avoiding the Beaujolais-like effect. > It sounds like you don't buy the argument that because Ada already has constructs with problems that are just as bad as the problems this new construct would introduce, these new problems are ok. Fair enough. >> Perhaps the solution is that if by doing this >>> you end up with a non-overloadable being hidden by an overloadable, >>> then both are hidden -- essentially the use-clause cancellation >>> rules. It would be odd to introduce a rule like this only for the implicit declarations associated with this new construct. On the other hand, there wouldn't be any compatibility issues with such a rule, whereas changing the legality of my original example would be an incompatible change. Are you arguing for the incompatible change? >> I'd like to see a plausible example where this rule would prevent >> something bad from happening. > > In my opinion, you have shown such an example above. ;-) Just replace > type D with this new package-reexporting gizmo. As you point out, any example where this gizmo occurs outside of a package spec fails the plausibility test. I generally don't like to add language restrictions that aren't associated with any definitional or implementation issues, so I would prefer not to see a requirement that this gizmo must occur in a package spec. Still, such a requirement wouldn't be completely unreasonable. The example could be modified by adding a package and then we'd have the badness we are looking for. How bad is this problem? > By the way, why would you ever want to use this new gizmo in a nested > block or procedure? Isn't essentially the same as a use clause in > that case? This seems like an advantage of the "subtype S is P.T with renames;" proposal. It is more generally useful because its semantics differ from those of "use type" within the scope where the renaming occurs. **************************************************************** From: Tucker Taft Date: Wednesday, December 17, 2008 8:17 PM > This seems like an advantage of the "subtype S is P.T with renames;" > proposal. It is more generally useful because its semantics differ > from those of "use type" within the scope where the renaming occurs. That was my point. If you really want the entire package to be visible, then a "use" clause in the clients isn't terribly painful. But if you just want the operations of a single type to be visible, then we don't have any such thing, except type derivation. This provides a lighter weight version of type derivation, which also happens to avoid some of the difficulties of type derivation, such as the situation where the new derived type is incompatible with the other things declared in the package, such as generics. And also the nasty case where there are two types declared in the package, and deriving from both produces a useless muddle of operations. **************************************************************** From: Steve Baird Date: Wednesday, December 17, 2008 8:46 PM Agreed, I left out an "As Tuck observed,". It is a little odd that these renaming things (package or type versions) differ from use clauses within the scope where they are declared with respect to hiding of declarations from enclosing scopes. package P is X : Float; end P; with P; procedure P_Client is X : Integer; type D is new Integer; Y : D; begin declare -- use P; -- declare renames for P.all; begin Y := D (X); If the "use P;" is uncommented, then the operand of the type conversion is still the Integer-valued X. If the "declare renames for P.all;" is uncommented instead, then the operand is the Float-valued X. Perhaps this is an error and the new construct(s) should be defined to be more consistent with use clauses in this respect. The simplicity of the "it's just like a bunch of renames" approach is appealing, but it looks like it has problems. **************************************************************** From: Randy Brukardt Date: Wednesday, December 17, 2008 8:56 PM > That was my point. If you really want the entire package to be > visible, then a "use" clause in the clients isn't terribly painful. While I understand your point, this argument doesn't fly. Besides the fact that some people don't like use clauses (one could make the argument that such people wouldn't use this new construct anyway, for similar reasons), it breaks the abstraction. If the package is supposed to be exporting a vector of some type, telling users that "oh, by the way, you'll need to add a use clause for some subpackage with a random name everywhere that you use our package". And the reason is because 'it isn't terribly painful'! That could be a lot of extra use clauses (or a lot of extra and unneeded visibility). > But if you just want the operations of a single type to be visible, > then we don't have any such thing, except type derivation. This > provides a lighter weight version of type derivation, which also > happens to avoid some of the difficulties of type derivation, such as > the situation where the new derived type is incompatible with the > other things declared in the package, such as generics. I have sympathy for this position as well. I think *both* of these things are problems, and *both* deserve solution. Bob's point is that if your packages are structured well, the two things are pretty equivalent. But losing exceptions and generics and deferred constants is annoying. (We had trouble with that in Claw, as iterator generics don't get inherited by type extensions. They have to be redefined, which is tough to do.) > And also the nasty case where there are two types declared in the > package, > and deriving from both produces a useless muddle of operations. I don't think it is worth trying to solve this problem; it would be a nasty set of visibility exceptions. Indeed, this is the best argument against the subtype case, because I don't even want to think about this (I can't imagine a simple way to do this in our compiler; all of these operations are declared in the symbol table, and this would be a case where we're supposed to somehow allow what is usually illegal. No thanks.) **************************************************************** From: Tucker Taft Date: Wednesday, December 17, 2008 9:11 PM > While I understand your point, this argument doesn't fly. Besides the > fact that some people don't like use clauses (one could make the > argument that such people wouldn't use this new construct anyway, for > similar reasons), it breaks the abstraction. If the package is > supposed to be exporting a vector of some type, telling users that > "oh, by the way, you'll need to add a use clause for some subpackage > with a random name everywhere that you use our package". And the > reason is because 'it isn't terribly painful'! That could be a lot of extra > use clauses (or a lot of extra and unneeded visibility). I'm afraid I am beginning to lose you. You may have to give an example. I don't know what you mean by "extra and unneeded" visibility. If you are allergic to use clauses, then you would reference the things that are *not* "brought out" by using the original subpackage name as a prefix. If you like use clauses, then you would do a "use" for the subpackage, presuming you had already done a "use" for the enclosing package, if you want direct visibility on generics, etc. If the construct "brought out" everything from the subpackage in the first place, then a "use" clause on the enclosing package would give you direct visibility on all of these things, but that would be exactly the same set of things you would get by doing the two "use" clauses in the first scenario. So I don't see any "extra or unneeded" visibility. It is exactly the same set of things that are directly visible in the two cases. > ... > And also the nasty case where there are two types declared in the > package, > >> and deriving from both produces a useless muddle of operations. > > I don't think it is worth trying to solve this problem; it would be a > nasty set of visibility exceptions. Indeed, this is the best argument > against the subtype case, because I don't even want to think about > this (I can't imagine a simple way to do this in our compiler; all of > these operations are declared in the symbol table, and this would be a > case where we're supposed to somehow allow what is usually illegal. No > thanks.) I don't follow your point here either. Again perhaps an example would clarify. I was merely suggesting that if you did two of these "subtype...with renames" (is that something like "friends ... with benefits"? ;-), you wouldn't get any complaints, because any shared operations would only be effectively renamed once into the scope. That is, subtype S1 is P.T1 with renames; subtype S2 is P.T2 with renames; would not complain if there is an operation in P that is a primitive of both T1 and T2. You wouldn't get two renames of it that somehow clobber each other. **************************************************************** From: Randy Brukardt Date: Wednesday, December 17, 2008 10:40 PM ... > I'm afraid I am beginning to lose you. You may have to give an > example. I don't know what you mean by "extra and unneeded" > visibility. > > If you are allergic to use clauses, then you would reference the > things that are *not* "brought out" by using the original subpackage > name as a prefix. If you like use clauses, then you would do a "use" > for the subpackage, presuming you had already done a "use" for the > enclosing package, if you want direct visibility on generics, etc. If > the construct "brought out" everything from the subpackage in the > first place, then a "use" clause on the enclosing package would give > you direct visibility on all of these things, but that would be > exactly the same set of things you would get by doing the two "use" > clauses in the first scenario. So I don't see any "extra or unneeded" > visibility. > It is exactly the same set of things that are directly visible in the > two cases. You aren't making any sense at all. "everywhere" is a lot of places; if you limit the scope of use clauses, you're going to have to put them in every place that you would otherwise use a single use clause. (I typically use them, if I use them at all, within blocks or subprograms.) Or (because you have a bunch of them rather than just one) you just have to give up and put them at the top level, but that is giving you "extra and unneeded" visibility. Having to write two things in place of one logical thing might not be "terribly painful" if you only have to do that in one place. If you have to do it in dozens, it gets messy. To see what I'm talking about, imagine that you have the following library package that should be logically viewed as one item: package Pack is type T is ...; -- Operations on T. -- Now, define "file of T" as part of this package: package T_IO is new Ada.Sequential_IO (T); -- declare use T_IO; -- Or whatever syntax you want for a package visibility. -- subtype File_Type is T_IO.File_Type with renames; -- Or whatever syntax you want for subtype visibility. end Pack; (I used Sequential_IO here because it has exceptions, but if you don't like that, imagine any package that declares its own exceptions.) Note that T_IO is a junk name that exists solely because Ada requires one here; we just want these entities to be available. That same is potentially true of the subtype name -- we probably don't want to give it a different name. with Pack; procedure Something is A : Pack.T_IO.File_Type; -- Full name. Definitely breaks the abstraction of "file of T" being part of Pack. use Pack; B : T_IO.File_Type; -- Still breaks the abstraction. use Pack, Pack.T_IO; C : File_Type; -- The declaration is OK now, but the *use clause* breaks the abstraction. begin ... exception when Use_Error => ... -- Only works with the third use clause. end; -- Now let's imagine that we use the package renaming scheme: with Pack; procedure Something is A : Pack.File_Type; -- Full name. Abstraction is fine. use Pack; B : File_Type; -- Abstraction is fine here, too. begin ... exception when Use_Error => ... -- Abstraction is fine here, three. end; -- But with the subtype renaming scheme, it isn't quite as perfect: with Pack; procedure Something is A : Pack.File_Type; -- Full name of subtype. Abstraction is fine. use Pack; B : File_Type; -- Abstraction is fine here, too. begin ... exception when Use_Error => ... -- Nope, no good. Have to go back to T_IO -- as in T_IO.Use_Error. Grrr. end; ... > I don't follow your point here either. Again perhaps an example would > clarify. I was merely suggesting that if you did two of these > "subtype...with renames" (is that something like "friends... > with benefits"? ;-), you wouldn't get any complaints, because any > shared operations would only be effectively renamed once into the > scope. That is, > > subtype S1 is P.T1 with renames; > subtype S2 is P.T2 with renames; > > would not complain if there is an operation in P that is a primitive > of both T1 and T2. You wouldn't get two renames of it that somehow > clobber each other. What the heck is a "shared operation" in language terms?? And how would you implement such a thing? You can't just search for homographs, as they could come up in other ways. What happens if someone writes: type T1 is new P.T1; subtype S2 is P.T2 with renames; I would expect that you'd still get the mess of crossover operations; how would the compiler tell that they're conflicting? Do you really mean that the presence of some other declaration pages away changes what's declared here? Is there any way to implement that without going back through all of the previously generated symbols and trying to eliminate extra ones? (We don't do that for anything else, so doing that would be weird and expensive.) There are thousands of such symbols in OOP programs (such as children of Claw), that's not going to be cheap. (And doing it the other way, by name is no better, because there are often thousands of the various operator symbols in a program.) The package idea has no such problems; it's nothing but an inherited use clause which means it's unlikely that there are many semantic issues with it. Besides, I don't see much reason to encourage giant messy packages (even if I write them from time-to-time). If you stick to one type to one package, the two ideas are equivalent, but the package one is easier to describe and doesn't have funny cases. **************************************************************** From: Tucker Taft Date: Wednesday, December 17, 2008 11:38 PM > What the heck is a "shared operation" in language terms?? And how > would you implement such a thing? You can't just search for > homographs, as they could come up in other ways. I didn't mean anything sophisticated here. All I meant was that if you used the "subtype ... with renames" twice from the same package in the same scope, it wouldn't end up creating two renames for the same operation. By a "shared" operation I mean one that is a primitive on both types. > ... > What happens if someone writes: > > type T1 is new P.T1; > subtype S2 is P.T2 with renames; > > I would expect that you'd still get the mess of crossover operations; > how would the compiler tell that they're conflicting? Yes, this would still produce a bit of a mess. I wasn't suggesting to fix this. Merely to say that if we provide this "subtype ... with renames," it shouldn't cause conflicts with other subtype-with-renames from the same package which are given in the same scope. > Do you really mean that the presence of some other declaration pages > away changes what's declared here? ... No. **************************************************************** From: Jean-Pierre Rosen Date: Thursday, December 18, 2008 3:03 AM > That's interesting: that suggests that this "gizmo" really *is* a use > clause > -- it's just one that is visible to clients (unlike the normal > variety). I wonder if it would be better to think of it that way (we > wouldn't need new visibility rules then). Something like: > declare use Pkg; > or (if we were willing to have a new keyword): > export use Pkg; > > and only allow that in a package (or generic package) specification > (or just have it have the same meaning as a usual use clause). > Alternatively, why not: pragma Inline (Pkg); which would declare everything from the package directly in the enclosing construct. Could be applied to an instantiation, too.... **************************************************************** From: Ed Schonberg Date: Thursday, December 17, 2008 4:13 PM This is confusing, it might mean that all subprograms within the package are to be inlined. However, the point is that we would want to associate this with an instance, so why not label the instance accordingly? Randy does not want some rule that declares that the new renaming construct should only appear in a restricted context, and I tend to agree. So let's use some new syntax on the instantiation itself: package V is new open G (X); -- your favorite new keyword here. Indicating that all the entities in G are available in the enclosing context, with the same name. It seems confusing to relate this to a use-clause: the use-clause affects locally the visibility of entities declared elsewhere, while here we want to create exportable views of those entities. **************************************************************** From: Bob Duff Date: Thursday, December 18, 2008 8:47 AM > That was my point. If you really want the entire package to be > visible, then a "use" clause in the clients isn't terribly painful. I think it is somewhat painful. I agree with what Randy said on this point. You haven't specified exactly what declarations this subtype-based idea brings out. By analogy with type derivation, I assume you mean primitive operations of the type. But surely you want class-wide ops as well. You really want everything related to the type. For example, you want exceptions that are raised by primitive operations of the type. My point is that "everything related to the type" is "everything in the package visible part" -- presuming the package is properly designed. **************************************************************** From: Bob Duff Date: Wednesday, December 17, 2008 8:47 AM > ...Are you arguing > for the incompatible change? I am not. I guess (after seeing Randy's point of view) that I am saying we don't want this new feature to have the same problems of confusing hiding. And thinking of it as a "transitive use clause" is the right direction in that regard. One possible syntax is "use Pack_Name.all;". > As you point out, any example where this gizmo occurs outside of a > package spec fails the plausibility test. I generally don't like to > add language restrictions that aren't associated with any definitional > or implementation issues, so I would prefer not to see a requirement > that this gizmo must occur in a package spec. > Still, such a requirement wouldn't be completely unreasonable. I agree 100% with the above wishy-washy opinion. ;-) **************************************************************** From: Bob Duff Date: Thursday, December 18, 2008 9:37 AM > So let's use some new syntax on the instantiation > itself: > > package V is new open G (X); -- your favorite new keyword here. I think the new gizmo Steve has proposed is not specific to instances. As I see it, it would allow you have an abstraction that is viewed as a single "thing" by clients, but is structured as many packages internally. The idea is that the client doesn't need to know about the many packages -- it just refers to one package that collects it all together. Some of those packages might be instances. Some might be physically nested, others children. You might rearrange their structure from time to time, while presenting an unchanging/compatible view to clients. There might be some "interesting" (in a good way) interactions with limited-with's, but I'm too busy to think about that right now... **************************************************************** From: Bob Duff Date: Thursday, December 18, 2008 9:38 AM > package V is new open G (X); -- your favorite new keyword here. If that keyword is reserved, Text_IO becomes illegal. ;-) **************************************************************** From: Tucker Taft Date: Thursday, December 18, 2008 12:27 PM I think we should make it a "use" clause, but with some kind of extra indication. I don't like ".all" as that automatically conjures up thoughts of access values, which seems completely unrelated to the situation. I could see: use P with renames; and presumably: use type T with renames; -- makes operators transitively visible If there is interest in having direct visibility on all primitive operations (and perhaps class-wide as well, though that would take some more thought), then probably we should just define a new kind of "use" clause, such as: use all type T [with renames]; -- makes all primitives (and classwides) directly visible, -- transitively so if "with renames" included. One interesting question is whether the primitives of a tagged type remain dispatching after such a transitive use clause. Clearly they do with a normal use clause, but they don't with a normal rename. Another possible syntax: use P for all; That is the "use P" is effectively exported "for all" contexts where the direct components of P are visible. Or: use and declare P; use P and declare; **************************************************************** From: Steve Baird Date: Thursday, December 18, 2008 12:38 PM > package V is new open G (X); -- your favorite new keyword here. My gut feeling is that restricting this new mechanism to instances does not seem like a good idea. Ditto for the general idea of associating this new visibility property with the declaration of the entity itself (as opposed to introducing a separate rename/use-clause-like construct). If pressed, I will try to justify this position. **************************************************************** From: Bob Duff Date: Friday, December 19, 2008 10:12 AM OK, I'd like to hear your justification. I mean, for the part starting "Ditto..." -- I definitely agree with the previous part, but I'm not sure about the "Ditto...". **************************************************************** From: Steve Baird Date: Friday, December 19, 2008 4:42 PM I was thinking about the case of a package spec consisting of nothing but expressionless renames, constraintless subtypes, named numbers initialized to other named numbers, etc. This, for example, might reflect a (static) implementation choice, as in with Task_Safe_Foo; package Foo is procedure Bar renames Task_Safe_Foo.Bar; ...; end Foo; or a restricted subset of a more general interface with Foo; package ReadOnly_Foo is subtype T is Foo.T; function Bar_Selector (X : T) return Integer renames Foo.Bar_Selector; -- no mention here of the corresponding procedure -- for assigning to the Bar attribute of a Foo.T end ReadOnly_Foo; or whatever. Suppose that we want to rename something like Ed's example, package V is new open G (X); -- your favorite new keyword here. Choices include: 1) A rename loses this special visibility property (just as a rename of a primitive operation can't be called via a dispatching call). This will make problems either for whoever has to write/maintain the ReadOnly_Foo spec (because they would have to declare explicit renames corresponding to those that are implicitly declared in Foo) or for the clients (if these explicit renames are omitted from the ReadOnly_Foo spec). I would view this as a bad thing. 2) A rename preserves this special visibility property. This means we have lost the ability to generate a "simple" rename in the case where that is all we want. This makes me nervous. This would also confuse the semantic equivalence between renames and actual-to-formal bindings for instantiations. Finally, consider a generic package that exports a rename of a formal instance; I suppose the set of declarations that are exported depends on the actual parameter. This all seems a bit complicated. 3) The rename itself gets new optional syntax so that the choice can go either way: package P renames Q [and all in P with out exception]; This pretty well takes us back to where we started. If we have to support this case, then why do we need anything else? As another point, consider the interaction with more nested packages. With the rename/use-clause approach, the following is straightforward: package Pkg is package Inner is package I is new G; end Inner; use or rename or somehow reexport Inner.I; end Pkg; It is not at all clear how to accomplish the same thing using the package I is new G and it glows in the dark; approach of including the specification of the special visibility property in the declaration of the package itself. The significance of this is, of course, open to debate. Does any of this seem convincing? **************************************************************** From: Bob Duff Date: Friday, December 19, 2008 10:10 AM > I think we should make it a "use" clause, but with some kind of extra > indication. I don't like ".all" > as that automatically conjures up thoughts of access values, which > seems completely unrelated to the situation. Shrug. That doesn't bother me. By the way, I'm a huge fan of compatibility, but I'm not allergic to new reserved words -- that's just about the least harmful kind of incompatibility. >...I could see: > > use P with renames; > > and presumably: > > use type T with renames; -- makes operators transitively visible Yes, the use-type version seems useful, now that you mention it. The above syntax seems tolerable, but "use all [type] X;" seems nicer (not meaning what you say below). > If there is interest in having direct visibility on all primitive > operations (and perhaps class-wide as well, though that would take > some more thought), I don't see any use in that. A typical package spec has 1 type, 15 subprograms, 1 exception, and 1 generic. Why would I want to import all but 2 of those things (or all but 4, if you leave out the class-wide ops)? > One interesting question is whether the primitives of a tagged type > remain dispatching after such a transitive use clause. The correct answer is "yes". >... Clearly they do with a normal use clause, but they don't with a >normal rename. > Another possible syntax: > > use P for all; > > That is the "use P" is effectively exported "for all" contexts where > the direct components of P are visible. > > Or: > > use and declare P; > > use P and declare; All of the above tolerable. **************************************************************** From: Ed Schonberg Date: Friday, December 19, 2008 4:19 PM > The above syntax seems tolerable, but "use all [type] X;" > seems nicer (not meaning what you say below). I also find "use all type P.T" clear and concise. But I'm confused about the semantics of the construct. A use clause makes the entities potentially visible in the current context, nothing else. I thought that we wanted this construct to also create a local entity that is visible outside of the current package. That's certainly the effect of the null derivation that we are trying to replace. I don't see how this new-fangled use-clause achieves this purpose. **************************************************************** From: Randy Brukardt Date: Friday, December 19, 2008 4:53 PM The idea is that the use clause is effective in clients of the package as well, such that: package P is ... package Vect is new Ada.Containers.Vectors (Some_Type); use all Vect; end P; with P; procedure One is A : P.Vector; -- The selection lets us see entities that are -- use-all-visible in P, as well as real declarations. begin null; end One; with P; use P; procedure Two is A : Vector; -- The "use P" also makes things that are use-all-visible in P visible. begin null; end Two; And other rules (in particular conflicting declarations) are handled as they are for use clauses. This is a pretty mimimal change to the language (how minimal to compilers I don't know and haven't thought about). The only thing that is truly new here is the selection followed by a use-all-visible item (the other way of course already happens). I recall that we had to work hard to disallow transitive use-clauses -- I think it is more natural for them to be transitive (depending on how you implement them), so I don't expect that part to be hard. **************************************************************** From: Ed Schonberg Date: Friday, December 19, 2008 5:08 PM OK, that's a very clear formulation, the "all" marks the use clause as transitive. I don't think the implementation is too complex, and the description has the great virtue of conciseness. I suppose that "use all" can be a context item as well? **************************************************************** From: Tucker Taft Date: Friday, December 19, 2008 5:28 PM > I also find "use all type P.T" clear and concise. But I'm confused > about the semantics of the construct. A use clause makes the entities > potentially visible in the current context, nothing else. I thought > that we wanted this construct to also create a local entity that is > visible outside of the current package. That's certainly the effect > of the null derivation that we are trying to replace. I don't see how > this new-fangled use-clause achieves this purpose. I think we have two separate threads going here, and I am afraid I am responsible for the confusion. I have seen a desire for some kind of "use" clause that makes enumeration literals directly visible. "use type" only makes operators visible, so I was suggesting something like "use all type" to pick up enumeration literals, and any other primitive subprograms. A relatively independent thread is one about creating "transitive" use clauses, or "reexporting" or whatever you want to call them. They work sort of like a bunch of renames, but I think we were concluding that they may need to have some special rules to deal with Beaujolais effects. One suggestion was "use P with renames" for a transitive "use" package clause. Once you establish this as merely a special "tweak" on a "use" clause, then it makes sense to generalize it to any kind of use clause, such as "use type P.T with renames;" I have confused the situation by talking about both at the same time. Sorry about that. Bob added a bit to the confusion by expressing a preference for the syntax "use all type P.T" to mean what I suggested to be "use type P.T with renames." I don't understand why "all" communicates transitivity to Bob, so I'll have to let him explain that. So we have two relatively separate ideas: Add a new kind of "use" clause oriented toward getting direct visibility on enumeration literals (and other primitives). Define a syntax that can be used with any kind of "use" clause to make it transitive, so that the entities made directly visible at the point of the use clause are also visible in the extended scope of the enclosing package spec (a transitive use clause would presumably be no different from a non-transitive one when not in a package spec). I don't know if that clarifies or confuses, perhaps both? ... ;-) **************************************************************** From: Tucker Taft Date: Friday, December 19, 2008 5:30 PM I don't find "use all" an intuitive syntax for transitivity. I could understand "all use" perhaps... ;-) **************************************************************** From: Bob Duff Date: Friday, December 19, 2008 5:31 PM ... > And other rules (in particular conflicting declarations) are handled > as they are for use clauses. Yes, that matches my understanding of the idea. But I think the wording will be tricky to get right. So there needs to be an implicit decl of Vector in P, so P.Vector makes sense. But the "conflicting declarations" issue is interesting. I think you want to be able to say: package P is ... package Int_Vect is new Ada.Containers.Vectors (Integer); use all Int_Vect; subtype Int_Vector is Int_Vect.Vector; package Boolean_Vect is new Ada.Containers.Vectors (Boolean); use all Boolean_Vect; subtype Boolean_Vector is Boolean_Vect.Vector; end P; and then P.Vector in clients should be illegal. But package P itself should be OK. And P.Append is just overloaded. > This is a pretty mimimal change to the language (how minimal to > compilers I don't know and haven't thought about). The only thing that > is truly new here is the selection followed by a use-all-visible item > (the other way of course already happens). I recall that we had to > work hard to disallow transitive use-clauses -- I think it is more > natural for them to be transitive (depending on how you implement > them), so I don't expect that part to be hard. And in message that came in while I was composing the above, Ed wrote: > I suppose that "use > all" can be a context item as well? Sure. Why not? Are there "interesting" interactions with "limited with"? **************************************************************** From: Steve Baird Date: Friday, December 19, 2008 9:32 PM > Are there "interesting" interactions with "limited with"? I would think these new constructs would be ignored in a limited view, just a use clauses and renames are already ignored. The case where the construct does not occur in a limited view but references a limited view is more interesting. In this example package P is type T is ... ; procedure Foo; package Inner is type TT is ... ; procedure Bax; end Inner; package I is new G; end P; limited with P; package Q is use all P; -- whatever the syntax is end Q; the use clause only allows clients of Q to name Q.T and Q.Inner, right? It doesn't matter whether the client can see a non-limited view of P. **************************************************************** From: Randy Brukardt Date: Friday, December 19, 2008 9:54 PM Ummm, use_clauses are illegal for limited views (8.4(5/2)). I'd surely expect that property to carry over, and thus the "use all" would be illegal here. So that's not very interesting, either. **************************************************************** From: Steve Baird Date: Saturday, December 20, 2008 1:56 AM Good point. I guess I'm still thinking of these things as being like renames, not use clauses. The use clause model is looking better and better. **************************************************************** From: Brad Moore Date: Sunday, December 21, 2008 1:06 PM A very interesting thread. I like the overall intent. I see this being useful as it is a problem I have run into often enough. I am wondering it there are some other cases worth considering. In particular, I am thinking it would be nice to be able to use this renaming concept to simplify access to a protected object or a task object exported from a generic. For example, suppose I have; generic type F is private; package G is protected P is procedure Foo; function Bar returns Integer; private X : F; end Driver; end G; with G; package Pkg1 is type T is null record; package I is new G (T); declare renames for I.P; -- Or whatever syntax works best end Pkg1; with Pkg1; package Pkg2 is Pkg1.Foo; -- Really calling Pkg1.I.P.Foo end Pkg2; I didn't recall seeing the ability to do this in the thread so far. Please accept my apology if this is already covered. This is actually something I could have used to enhance the example I just provided in my first email under the subject of private generic actuals. **************************************************************** From: Brad Moore Date: Friday, January 9, 2009 12:29 AM This email actually applies to both AI05-0074 and this thread. One of the problems that AI05-0074 is trying to solve is being able to instantiate a generic in the visible part of a package using a private type. The quoted example is: "generic type Elem is private; with function Hash(El : Elem) return Integer; package Hashed_Sets is type Set is private; function Union(Left, Right : Set) return Set; ... package Signature is new Set_Signature(Elem, Set); private type Set is record ... end record; end Hashed_Sets; The problem is that we can't do the instantiation of Set_Signature where we would want to do so, because the instantiation freezes the type "Set" prematurely." The AI states that the there is a workaround in Ada 2005, which is stated as; "Ada 2005 provides solutions to these problems, but they are not ideal. For the first example, making the instantiation a child unit solves the problem. However, this is annoying, because accessing the instance requires an extra with and instantiation (since children of generics must be generic): generic type Elem is private; with function Hash (El : Elem) return Integer; package Hashed_Sets is type Set is private; function Union(Left, Right : Set) return Set; ... private type Set is record ... end record; end Hashed_Sets; generic package.Hashed_Sets.Signature is package The_Signature is new Set_Signature(Elem, Set); end Hashed_Sets.Signature; A user of Hashed_Sets must with and instantiate Hashed_Sets.Signature in order to access the instance. " It seems to me that a better work around does exist, which involves the use of nested packages. generic type Elem is private; with function Hash (El : Elem) return Integer; package Hashed_Sets is package Sets is type Set is private; function Union(Left, Right : Set) return Set; private type Set is record ... end record; end Sets; type Set is new Sets.Set; package The_Signature is new Set_Signature(Elem, Set); end Hashed_Sets; No messy child packages to deal with. It all happens in one place. This is somewhat like the end private alternative, in that a second visible part occurs after the end of a private section, except that its available today in Ada 2005. This should work if it is possible to derive a new type from the private type. If you can't do the derivation, for example because the type is a protected type, then you can declare a subtype instead, but then you run into the renaming issue. eg generic type Elem is private; with function Hash (El : Elem) return Integer; package Hashed_Sets is package Sets is type Set is private; function Union(Left, Right : Set) return Set; private type Set is record ... end record; end Sets; subtype Set is Sets.Set; function Union (Left, Right : Set) return Set renames Sets.Union; ... package The_Signature is new Set_Signature(Elem, Set); end Hashed_Sets; Now here is where this email thread kicks in. Hopefully all of the renames could be replaced by one of these new super-duper rename clauses. This sort of feels to me like a better work around to the problem than a solution, but others may feel differently. Comments? **************************************************************** From: Brad Moore Date: Tuesday, January 20, 2009 10:39 PM > I think you want to be able to say: > > package P is > ... > package Int_Vect is new Ada.Containers.Vectors (Integer); > use all Int_Vect; > subtype Int_Vector is Int_Vect.Vector; > > package Boolean_Vect is new Ada.Containers.Vectors (Boolean); > use all Boolean_Vect; > subtype Boolean_Vector is Boolean_Vect.Vector; > > end P; > > and then P.Vector in clients should be illegal. > But package P itself should be OK. > And P.Append is just overloaded. First I have a question, then I will describe a problem that I can see, then I have a suggestion for how it might be possible to address/improve the situation. First the question: Will it be possible to complete an incomplete type with a "use all" statement? e.g. -- A generic implementing a protected interface private with Protected_Queue; package Pkg is type T is private; private type Queue_Type; type T is record Queue : access Queue_Type; end record; package I is new Protected_Queue (T); use all I; -- Queue_Type is exported from package I, and completes the -- incomplete type declaration. -- Can't derive a new type here, i.e., -- type Queue_Type is new I.Queue with null record; -- ERROR Cannot derive a new type from a protected type or -- interface. end Pkg; Without this capability this is a problem without any workaround that I can see. You cannot derive from a synchronized tagged type, for example. You cannot use a subtype, because the incomplete type needs a completing type declaration, not a subtype declaration. Yet this seems like something users could run into easily enough. Suppose that type T has a task component that writes to the queue. Some sort of concurrency support is needed. One option might be to allow synchronized tagged types to be extended, but I don't feel that is the correct solution to this particular problem. We don't want to have to derive a new type here, we just want to be able to use the one we created with the instantiation. Assuming the use all can complete the incomplete type, this solves the problem above, but now suppose there are two difference instantiations of the same generic package declared and referenced in type T. The name collisions would be a problem, and you wouldn't be able to have two incomplete type declarations with the same name. Once again, subtype declarations wouldn't help you here. The problem once again becomes unsolvable. -- A generic implementing a protected interface -- Similar to Ada.Containers.Hashed_Maps, but a protected type private with Protected_Hashed_Map; package Pkg is type T is private; private type Map; -- Need an incomplete type for the Integer Map type type Map; -- Need an incomplete type for the float Map type -- Cant use the same name for both! type T is record Integer_Map : access Map_Type; Float_Map : access Map_Type; end record; package I is new Protected_Hashed_Map (Key => T, Element => Integer, ...); use all I; package J is new Protected_Hashed_Map (Key => T, Element => Float, ...); use all J; -- Broken end Pkg; Both of these examples seems to want something different than what the "use all" statement would provide here. The use all enabled us to get rid of having to derive a new type. That's an improvement. But we still are being forced to export the entire instantiated packages into the main package. Don't get me wrong, the use all clause is a great idea, and I can see it having many uses, but in this particular case it seems to be overkill for what we are trying to achieve. All we really want here is to just be able to use the types that have been instantiated. Instead of trying to bring the water to the horse, what if we instead tried to bring the horse to the water? The idea here is to be able to declare what I would call a scoped incomplete type, for lack of a better term. The incomplete type declaration could optionally include package name qualifiers, which indicates that the declaration will be completed by a type declaration in a matching named scope. eg. Something like; incomplete_type_declaration ::= TYPE {[defining_program_unit_name].}defining_identifier [discriminant_part] [IS TAGGED]; Then the above example could be fixed to look like; -- A Map abstraction implementing a protected interface with Protected_Hashed_Map; package Pkg is type T is private; private type I.Map; -- Need an incomplete type for the Integer Map type type J.Map; -- Need an incomplete type for the float Map type -- Now we have a unique name for both incomplete types type T is record Integer_Map : access I.Map; Float_Map : access J.Map; end record; package I is new Protected_Hashed_Map (Key => T, Element => Integer, ...); package J is new Protected_Hashed_Map (Key => T, Element => Float, ...); -- I.MAP is completed by the I instantiation -- J.MAP is completed by the J instantiation end Pkg; In this case, there would be no need to use a "use all" statement. I don't see this as a replacement for use all, just as an extra tool that can be used in addition to the "use all" tool. If such a tool existed I would have also used it in the first example with the protected queue rather than use a use all statement. Does this idea seem like it would be worth pursuing? Maybe if this feature existed, we wouldn't need to worry about having to have the use all feature be able to complete an incomplete type declaration. **************************************************************** From: Brad Moore Date: Tuesday, January 20, 2009 11:02 PM ... > First the question: > > Will it be possible to complete an incomplete type with a "use all" > statement? A use clause doesn't complete anything, and the model as I see it is that this is just a super use clause. So I surely would not expect it to complete anything. ... > -- A generic implementing a protected interface > private with Protected_Queue; > > package Pkg is > type T is private; > private > type Queue_Type; > type T is > record > Queue : access Queue_Type; > end record; > package I is new Protected_Queue (T); > use all I; > -- Queue_Type is exported from package I, and completes the > -- incomplete type declaration. > > -- Can't derive a new type here, i.e., > -- type Queue_Type is new I.Queue with null record; > -- ERROR Cannot derive a new type from a protected type or > -- interface. > end Pkg; > > Without this capability this is a problem without any workaround that > I can see. You cannot derive from a synchronized tagged type, for > example. > You cannot use a subtype, because the incomplete type needs a > completing type declaration, not a subtype declaration. Why not put the instantiation first (I'm presuming that we find a way to do this, it is too fundamental to container usage to not solve it somehow - without it, we can't have real iterators or container interfaces). Using my limited instantiation idea, for instance: package Pkg is type T is private; private package I is limited new Protected_Queue (T); type T is record Queue : access I.Queue_Type; end record; package I is new Protected_Queue (T); end Pkg; (I took the use clause out 'cause I don't like them much, but you could leave it in, of course.) Whatever solution we come up with ought to be able to do this. ... > Does this idea seem like it would be worth pursuing? > Maybe if this feature existed, we wouldn't need to worry about having > to have the use all feature be able to complete an incomplete type > declaration. It doesn't seem necessary, assuming we solve the basic problem. And there are all kinds of traps waiting if we try to expand the meaning of incomplete types -- we had a lot of places where ugly cases couldn't happen because everything has to occur in the same scope. Your examples here do tell me one thing, however: the workarounds to fixing the instance problem directly are never going to handle all of the cases. (The problem is, that all of the instance problem solutions don't handle all of the cases, either.) We really have to solve the AI05-0074-x problem somehow. **************************************************************** From: Brad Moore Date: Wednesday, January 21, 2009 10:34 AM > Whatever solution we come up with ought to be able to do this. I agree. > Your examples here do tell me one thing, however: the workarounds to > fixing the instance problem directly are never going to handle all of > the cases. (The problem is, that all of the instance problem solutions > don't handle all of the cases, either.) We really have to solve the > AI05-0074-x problem somehow. Note also, the second example using the hashed map generic is a bigger problem than just for synchronized tagged types. It applies to any types. You would have the same problem if you actually tried to do the same thing with the existing Ada.Containers.Hashed_Maps container. This second case is really a problem that appears to have existed all along since the dawn of Ada. Though the fact that it is being reported now seems to suggest that there are not a lot of cases of people running into this particular problem. However, the problem is exacerbated with Ada 2005 since we now have containers such as the Hashed Map container, making it more likely that people could run into the problem. It may be that in the past, people just shrugged their shoulders, and redesigned their software to work some other way. The private instantiation proposal solves these problems, but it currently has to straddle the visible/private boundary. e.g. private with Ada.Containers.Hashed_Maps; package Pkg is type T is private; package I is new private Ada.Containers.Hashed_Maps (others => private); package J is new private Ada.Containers.Hashed_Maps (others => private); private type T is record Integer_Map : access I.Map; Float_Map : access J.Map; end record; package I is new Ada.Containers.Hashed_Maps (Key => T, Element => Integer, ...); package J is new Ada.Containers.Hashed_Maps (Key => T, Element => Float, ...); end Pkg; I believe my proposal can be reworked fairly easily with only fairly minor alterations so that it is not restricted to crossing the visible/private boundary. The syntax would be modified to change an occurrence of the word "private" to "limited", as in. limited_instantiation_declaration ::= PACKAGE defining_program_unit_name IS NEW LIMITED generic_package_name [deferred_generic_actual_part]; deferred_generic_actual_part ::= (deferred_generic_association {, deferred_generic_association}) deferred_generic_association ::= [generic_formal_parameter_selector_name =>] explicit_generic_actual_parameter | [generic_formal_parameter_selector_name =>] PRIVATE | others => PRIVATE The (OTHERS => PRIVATE) syntax would remain intact, but could only be used in cases where the instantiation straddles the visible/private bounddary. Otherwise, the instantiation and completion can also occur both in the visible part, or both in the private part. e.g. private with Ada.Containers.Hashed_Maps; package Pkg is type T is private; private package I is new limited Ada.Containers.Hashed_Maps (Key => T, Element => Integer, ...); package J is new limited Ada.Containers.Hashed_Maps (Key => T, Element => Float, ...); type T is record Integer_Map : access I.Map; Float_Map : access J.Map; end record; package I is new Ada.Containers.Hashed_Maps (Key => T, Element => Integer, ...); package J is new Ada.Containers.Hashed_Maps (Key => T, Element => Float, ...); end Pkg; I'm also thinking that a shorthand form of the others=>private syntax might be allowed where you simply don't have to supply any parameters. This shorthand form could be used anywhere the others=>private could be used, but in addition could be used in cases such as this, as in; private with Ada.Containers.Hashed_Maps; package Pkg is type T is private; private package I is new limited Ada.Containers.Hashed_Maps; package J is new limited Ada.Containers.Hashed_Maps; type T is record Integer_Map : access I.Map; Float_Map : access J.Map; end record; package I is new Ada.Containers.Hashed_Maps (Key => T, Element => Integer, ...); package J is new Ada.Containers.Hashed_Maps (Key => T, Element => Float, ...); end Pkg; I am currently reworking my proposal to include some other ideas. Most notably, I think the May_Be_Partial pragma should allow deriving a new type from a formal that has the pragma applied within the visible part of the generic spec. (i.e. Freezing does not occur for type derivations that have a May_Be_Partial pragma applied.) generic type T is private; pragma May_Be_Partial (T); package Pkg is type Y is new T; end Pkg; My reasoning is that if the actual for T is a partial type, then it should be OK to derive a new type from it, because you can derive from a partial type. If the actual for T isn't a partial type, it should still be OK, because the May_Be_Partial pragma ensures that there will be no objects or expressions of that type in the package specification. I think freezing issues can be avoided in either case. If the pragma is not applied to a formal type, then deriving from that type will cause freezing to occur, as it does currently in the standard. **************************************************************** From: Steve Baird Date: Wednesday, January 28, 2009 7:47 PM > I think the semantics is pretty clear (a reexporting use clause), but > surely it would be good to write that up. (Shouldn't be too hard.) One more corner case to clear up ... Should we allow a child unit to conflict with a homograph that is made visible in a package spec via this new construct? Consider: package P is type R is record F : Integer; end record; X : R; end P; with P; package Q is use all P; -- syntax TBD end Q; package Q.X is -- legal? F : Integer; end Q.X; with Q; -- with Q.X; procedure Foo is begin Q.X.F := 17; end Foo; If the declaration of the child unit is legal, then does the name Q.X.F mean two different things depending on whether the with of Q.X is commented out or not? This would be particularly confusing in the case where the name and the context_clause are in different compilation units (e.g., the name Q.X.F is in a subunit and the context clause of an enclosing unit is modified by adding/deleting a with of Q.X). However this ends up being handled, the case of a "sprouted" generic (RM 10.1.1(9/2)) should be treated consistently: package P is generic package G is end G; end P; with P; generic package Q is use all P; -- syntax TBD end Q; with Q; package I1 is new Q; generic package Q.G is -- legal? end Q.X; with I1; -- with Q.G; procedure Foo is package I2 is new I1.G; begin null; end Foo; **************************************************************** From: Randy Brukardt Date: Saturday, January 31, 2009 11:26 PM > One more corner case to clear up ... Isn't there always? :-) > Should we allow a child unit to conflict with a homograph that is made > visible in a package spec via this new construct? > > Consider: > > package P is > type R is record F : Integer; end record; > X : R; > end P; > > with P; > package Q is > use all P; -- syntax TBD > end Q; > > package Q.X is -- legal? > F : Integer; > end Q.X; > > with Q; > -- with Q.X; > procedure Foo is > begin > Q.X.F := 17; > end Foo; > > If the declaration of the child unit is legal, then does the name > Q.X.F mean two different things depending on whether the with of Q.X > is commented out or not? > This would be particularly confusing in the case where the name and > the context_clause are in different compilation units (e.g., the name > Q.X.F is in a subunit and the context clause of an enclosing unit is > modified by adding/deleting a with of Q.X). Humm. My original thought was that this should work just like use clauses, warts and all. After all, you make this sound nasty and original; but it already happens for use clauses and child units, and has since Ada 95 was introduced. We've survived. :-) For example: package P is type R is record F : Integer; end record; X : R; end P; package Q is end Q; package Q.X is F : Integer; end Q.X; with P; use P; -- with Q.X; procedure Q.Z is begin X.F := 17; end Q.Z; If you add the with clause for Q.X, package X becomes directly visible and hides the use visible object from P. But the programmer probably never even considered the idea of calling the package plain X (they've always thought of it as Q.X), so they're likely to be very confused. All of the same concerns apply about doing it in another unit (it can also happen when an unrelated unit is converted to a child). This case does seem a bit worse, because it doesn't require a child unit to get in trouble; and it happens in selected notation (as we didn't have use-visible selected entities before). I guess the question has to be "how nasty is the rule preventing children?". That is, how hard is to word, and how hard is it to understand. If it's relatively simple, perhaps we should simply ban the confusion (we did similar things with "private with" even though the similar problems with children are not prevented). But a big mess it isn't worth. I worry that it might get very messy once you bring "sprouting" into the mix. I'll trust you to work that out. **************************************************************** From: Bob Duff Date: Monday, February 2, 2009 2:15 PM > After all, you make this sound nasty and original; but it already > happens for use clauses and child units, and has since Ada 95 was introduced. You can come up with similar surprises even in Ada 83. And with subunits, you can make the surprise cross source-file boundaries. Ada 95 makes it a little worse, with child units. >...We've > survived. :-) I tend to agree with that attitude. I don't like it, but as you point out, we already have similar cases, and we can't fix the whole problem compatibly. These are really Beaujolais-like effects. Ichbiah came up with some excellent visibility rules for use_clauses, and prevented Beaujolais effects (with a few minor bugs, fixed in Ada 95). But I wonder why he defined "Beaujolais effect" purely in terms of use_clauses -- it should be defined more broadly, in terms of all visibility-related things. **************************************************************** From: Robert I. Eachus Date: Monday, February 2, 2009 6:28 PM >You can come up with similar surprises even in Ada 83. And with >subunits, you can make the surprise cross source-file boundaries. Ada >95 makes it a little worse, with child units. Reminds me of a very old gotcha in Ada. I think I originally wrote it as part of a test of compiler overload resolution. Dave Emery, Rich Hilliard and I extracted one case and ran it against Ada compilers on display at a SIGAda meeting. The reaction of almost all who saw their compiler reject it was: "That must be a compiler bug--I'll write it up." We then assured the astonished demonstrator that there was no bug their compiler was right to reject the program: procedure Hiding is procedure Foo (X: in Integer := 1) is begin null; end; begin declare procedure Foo is begin null; end; begin Foo; end; end Hiding; The outer Foo can be called by providing an explicit parameter, the inner Foo never can. Is this a bug in Ada that you can declare a subprogram that can't be called? Not really, this case is pretty harmless. The case where both subroutines are declared in the same scope is similar, and again, a surprise at compile time, but relatively harmless. There are variations using use clauses and/or nested packages, which were the original purpose of the test, but none of them result in a subprogram being totally uncallable. Why bring this up? The old joke: "Doctor, Doctor, it hurts when I do this!" "Well, don't do it then." There are lots of areas in Ada that may be of great interest to language lawyers, but which Ada programmers have learned to avoid. If not because they are bad software engineering, because any rocks they run into in those shoals will make them look foolish later. **************************************************************** From: Steve Baird Date: Friday, February 6, 2009 6:18 PM The attached [Version /01 of the AI] is a first cut at wording for this AI. The syntax for this construct is still very much up in the air; I just went with "use all" here in order to have a specific proposal. Note that the new super-powers for "use all type" are completely orthogonal to the rest of the proposal. We are using this new reserved word to indicate two new properties which are really unrelated (other than that they both increase visibility). The only hard part of separating the two properties would be deciding on syntax. **************************************************************** From: Tucker Taft Date: Thursday, March 12, 2009 6:05 AM I am growing pretty fond of the "use package P is ..." proposal as a way to both define and then "open" a subpackage, particularly since it solves the private-type instantiation problem so nicely. I had a few additional thoughts about the proposal: 1) Don't make the subpackage name itself directly visible when the enclosing package is "use"d -- just make the contents of the subpackage directly visible. Rationale: I was somewhat enamored of allowing the subpackage name to be "<>" but I understand Erhard's objection to it, and I also realize if there were more than one, defining the body for the subpackage would be ambiguous. The main reason for wanting "<>" was so that the subpackage name didn't pollute the namespace. Then I realized that if we simply didn't make the subpackage name directly visible when the enclosing package were "use"d, it would solve the problem. This way you can call the subpackage "Inner" or any name you find suggestive of its purpose, but if the enclosing package is "use"d, the subpackage name won't end up creating any problems by "cancelling" other use-visible entities that happen to have the same name. It also seems to make some intuitive sense. The whole point of this use package P ... syntax is that the contents of P are made directly visible when the enclosing package is "use"d. It seems reasonable then to "subtract" the name of the subpackage itself if you are getting all of its contents. Example: package Abstraction is use package Inner is -- "Inner" is *not* made directly -- visible by a "use Abstraction" type ADT is private; ... private type ADT is ... end Inner; package Signature is new Container_Signature(ADT, ...); -- Instantiate signature use package ADT_Vectors is Vectors(ADT, ...); -- Create vector of ADT subtype ADT_Vector is ADT_Vectors.Vector; -- Define longer name end Abstraction; 2) Allow "use" in front of a package rename, with the effect of "open"ing the named package. Rationale: This seems consistent, since there are three ways to create something that looks like a subpackage -- define one, instantiate one, or rename one. Furthermore, it gives us the "transitive" use, but with a syntax that is clearly distinct from the current "use" clause syntax. And finally, by providing a name to the renamed package, we avoid running into the same problem that Erhard identified with package instantiations, so there is always a way to handle name collisions that result from transitive "use." Example: with Useful_Stuff, More_Useful_Stuff; package Combination is use package Part1 renames Useful_Stuff; use package Part2 renames More_Useful_Stuff; end Combination; 3) Consider using syntax similar to aspect specification to rename components as part of doing a "use package": Rationale: If you are going to "open" a package into the enclosing scope, you may periodically want to rename some of its components to avoid collisions. Example: use package ADT_Vectors is new Vectors(ADT, ...) with ADT_Vector renames Vector, ADT_Cursor renames Cursor; This would instantiate Vectors and then "open" up the instance, but with the two types renamed to better reflect their meaning in the enclosing scope. This would only be allowed on package instantiations and renames, not on newly defined nested packages. **************************************************************** From: Bob Duff Date: Thursday, March 12, 2009 8:47 AM > Rationale: I was somewhat enamored of allowing the subpackage name to > be "<>" but I understand Erhard's objection This is the third time in as many days that I've seen proposals to add new meanings to the <> symbol! **************************************************************** From: Tucker Taft Date: Thursday, March 12, 2009 1:08 PM But in my case it was just a strawman, and I am not recommending it. But it is a very nice symbol, after all ... ;-) **************************************************************** From: Steve Baird Date: Thursday, March 12, 2009 3:24 PM > I am growing pretty fond of the "use package P is ..." > proposal as a way to both define and then "open" a subpackage, > particularly since it solves the private-type instantiation problem so > nicely. I like it too. I think it is a clean solution to an important problem. > I had a few additional thoughts about the proposal: > > 1) Don't make the subpackage name itself directly visible when the > enclosing package is "use"d -- just make the contents of the > subpackage directly visible. > I don't like this idea, but I don't feel strongly about it. I don't see any definitional problems with it - it just seems strikes me as unnecessarily irregular. I would be interested in knowing whether others feel that I am underestimating the impact of the name space pollution problem, > Rationale: I was somewhat enamored of allowing the subpackage name to > be "<>" but I understand Erhard's objection to it, and I also realize > if there were more than one, defining the body for the subpackage > would be ambiguous. > The main reason for wanting "<>" was so that the subpackage name > didn't pollute the namespace. Then I realized that if we simply > didn't make the subpackage name directly visible when the enclosing > package were "use"d, it would solve the problem. This way you can > call the subpackage "Inner" or any name you find suggestive of its > purpose, but if the enclosing package is "use"d, the subpackage name > won't end up creating any problems by "cancelling" other use-visible > entities that happen to have the same name. It also seems to make > some intuitive sense. The whole point of this use package P ... > syntax is that the contents of P are made directly visible when the > enclosing package is "use"d. > It seems reasonable then to "subtract" the name of the subpackage > itself if you are getting all of its contents. > > Example: > > > package Abstraction is > > use > package Inner is > -- "Inner" is *not* made directly > -- visible by a "use Abstraction" > type ADT is private; > ... > private > type ADT is ... > end Inner; > > > package Signature is > new Container_Signature(ADT, ...); > -- Instantiate signature > > use > package ADT_Vectors is Vectors(ADT, ...); > -- Create vector of ADT > > subtype ADT_Vector is ADT_Vectors.Vector; > -- Define longer name > end Abstraction; > > > 2) Allow "use" in front of a package rename, with the effect of > "open"ing the named package. > > Rationale: This seems consistent, since there are three ways to > create something that looks like a subpackage -- define one, > instantiate one, or rename one. Furthermore, it gives us the > "transitive" use, but with a syntax that is clearly distinct from the > current "use" clause syntax. > And finally, by providing a name to the renamed package, we avoid > running into the same problem that Erhard identified with package > instantiations, so there is always a way to handle name collisions > that result from transitive "use." > Is it clear that we need this generality? This would require adding wording to cope with various pathological cases that this would make possible (e.g. a rename of a limited view of a package, a rename of a package whose spec encloses the rename). All this is doable, but I think that it would introduce some definitional complexity. This feature also has a very high potential for misuse. Use clauses can already be used to write code which is difficult to read; this construct would make matters even worse in this respect. Incidentally, should the following be legal? package Outer is use package Inner is type T is new Integer; end Inner; end Outer; limited with Outer; package Snort is type T_Ref is access Outer.T; end Snort; In other words, is the distinction between a "use" and a "non-use" package retained in the limited view of an enclosing package? > Example: > > with Useful_Stuff, More_Useful_Stuff; > package Combination is > > use package Part1 renames Useful_Stuff; > > use package Part2 renames More_Useful_Stuff; > > end Combination; > > 3) Consider using syntax similar to aspect specification > to rename components as part of doing a "use package": > > Rationale: If you are going to "open" a package into the enclosing > scope, you may periodically want to rename some of its components to > avoid collisions. > > Example: > > use package ADT_Vectors is new Vectors(ADT, ...) > with > ADT_Vector renames Vector, > ADT_Cursor renames Cursor; > > This would instantiate Vectors and then "open" up the instance, but > with the two types renamed to better reflect their meaning in the > enclosing scope. This would only be allowed on package instantiations > and renames, not on newly defined nested packages. This seems like a fairly big change, because it is no longer so simple to define this construct just in terms of use-visibility. As I imagine it, adding the word "use" to a package declaration would have only three effects: 1) Visible declarations within the inner package spec become use-visible within the enclosing package. Informally, this can be thought of as meaning that an implicit use clause is inserted immediately after the package declaration, as in package P is use package Q is X : constant Integer := ... ; end Q; use Q; -- implicitly inserted Y : Integer := X; end P; 2) A use clause which applies to the enclosing package also applies to the inner package. Informally, this can be thought of as meaning that an implicit use clause is inserted immediately after any use clause which applies to the enclosing package, as in with P; package Gronk is use P; use P.Q; -- implicitly inserted Z : Integer := X; end Gronk; 3) At the point of resolving an expanded name whose prefix denotes the enclosing package, any visible declarations of the inner package become "use visible by selection", which is a term defined in the current (now obsolete) version of AI05-0135. Informally (and I mean *really* informally), this can be thought of as meaning that an implicit use clause is inserted at the point of the dot in the selected name, as in with P; package Snuffle is W : Integer := P . use P.Q; -- implicitly inserted X; end Snuffle; The change you are proposing does not seem to fit this model well. I'm not trying to let the implementation dictate the specification here. I'm saying that we have a fairly clean model which - should not require a lot of complicated RM wording - should be possible for users to understand and I'm reluctant to give that up. This approach I've outlined depends on the fact that we are only making existing declarations use-visible - we are not introducing renames or any other new implicitly-declared declarations. Rename-based (as opposed to use-visibility-based) approaches to this problem were discussed a while back (that was the approach that I initially had in mind) and have been abandoned for good reasons having to do with homograph collisions. If we really wanted to include this feature, I'd think about a "like a rename, but different" approach where the identifier "Vector" in your example becomes use-visible by the name of "Adt_Vector". I'm not advocating this approach - I'd rather not include the feature - but this seems like the way to define it if we decide that we really want the feature. I don't think mixing implicit rename declarations into a definition that is based on use-visibility is the way to go. **************************************************************** From: Gary Dismukes Date: Thursday, March 12, 2009 3:55 PM > I like it too. > I think it is a clean solution to an important problem. I haven't completely decided whether I like it yet, but I certainly like it better than other alternatives proposed for solving the "private instantiation" problem, so from that point of view it's appealing. > > I had a few additional thoughts about the proposal: > > > > 1) Don't make the subpackage name itself directly visible when the > > enclosing package is "use"d -- just make the contents of the > > subpackage directly visible. > > > > I don't like this idea, but I don't feel strongly about it. I don't > see any definitional problems with it - it just seems strikes me as > unnecessarily irregular. > I would be interested in knowing whether others feel that I am > underestimating the impact of the name space pollution problem, I also find that odd. For one thing, it seems weird to have a name that can't be referenced. It seems like it should still be possible to name the entities in the package by selection from the package, though I realize that's unnecessary once they're "reexported". As you say, it feels irregular. **************************************************************** From: Randy Brukardt Date: Thursday, March 12, 2009 4:07 PM > I haven't completely decided whether I like it yet, but I certainly > like it better than other alternatives proposed for solving the > "private instantiation" problem, so from that point of view it's > appealing. So far, I think it's a good idea. I've never liked using derivation to make things visible (too tricky for my taste, and you get types that you don't need or want). But since I don't like "use" clauses much, I'll reserve final judgment until we get further along with the definition. ... > I also find that odd. For one thing, it seems weird to have a name > that can't be referenced. It seems like it should still be possible > to name the entities in the package by selection from the package, > though I realize that's unnecessary once they're "reexported". > As you say, it feels irregular. I agree with Gary and Steve. It doesn't seem necessary to add complexity to this proposal. If you really are worried about name pollution and you are pretty sure that you'll never want to use the package name, just name it something like "package Dont_Ever_Use_This_Package_Name_In_Your_Code" or "package This_Package_Has_No_Name_and_Dont_Ever_Use_It" and you should not have to worry about conflicts. (And yes, I have done that in a few cases in my code.) **************************************************************** From: Tucker Taft Date: Thursday, March 12, 2009 4:02 PM ... > I don't like this idea, but I don't feel strongly about it. I don't > see any definitional problems with it - it just seems strikes me as > unnecessarily irregular. > I would be interested in knowing whether others feel that I am > underestimating the impact of the name space pollution problem, I think the namespace pollution issue is a big one. If we don't do this, I foresee people using all kinds of weird names to try to be sure they don't have any collisions, and that seems like a waste of time and something that will make such packages look bogus. Hiding the name of the subpackage itself from use visibility, for a subpackage that is "used at birth" (or "always open") feels like a much cleaner approach, and allows the user to pick a name for the subpackage that is descriptive rather than intentionally obscure. I don't see it as "irregular" but rather I see it as a fairly natural consequence of having its contents always use-visible. >> 2) Allow "use" in front of a package rename, with the effect of >> "open"ing the named package. >> ... > > Is it clear that we need this generality? > > This would require adding wording to cope with various pathological > cases that this would make possible (e.g. a rename of a limited view > of a package, a rename of a package whose spec encloses the rename). Why are these pathological? Can you explain what makes them worse than a rename followed by a normal "use" of the package? (My mind isn't as fiendish as yours, I suspect.) > All this is doable, but I think that it would introduce some > definitional complexity. I would agree if this adds complexity it isn't worth it. But if not, it just feels unnatural if you see the three ways of defining a subpackage, but we allow "use" in front of two of them but not the third, even though a "normal" use clause is legal with any of the three. > This feature also has a very high potential for misuse. > Use clauses can already be used to write code which is difficult to > read; this construct would make matters even worse in this respect. I'm not sure I see the issue here, so perhaps an example might help. > > Incidentally, should the following be legal? > > package Outer is > use package Inner is > type T is new Integer; > end Inner; > end Outer; > > limited with Outer; > package Snort is > type T_Ref is access Outer.T; > end Snort; > > In other words, is the distinction between a "use" and a "non-use" > package retained in the limited view of an enclosing package? It would be weird if when you changed from non-limited to limited or vice versa, you had to insert/remove the name of the subpackage. I don't see a big implementation burden here, and thus far limited views have been subsets of the full view. It would be annoying if they were not just subsets, but really markedly different from the full view. ... >> 3) Consider using syntax similar to aspect specification >> to rename components as part of doing a "use package": ... > This seems like a fairly big change, because it is no longer so simple > to define this construct just in terms of use-visibility. That's why I said "consider using" ;-) The rationale for this is that after instantiating a container, users are probably going to have to have several "subtype" declarations, as illustrated above. It just seems preferable to do it all in one fell swoop. I think the renames could be implicitly inserted *inside* the subpackage, just prior to any private part. That would mean that you would still rely on use-visibility rather than "rename" visibility" for any effect outside the package. **************************************************************** From: Bob Duff Date: Thursday, March 12, 2009 3:39 PM > I like it too. Me, three. > I think it is a clean solution to an important problem. > > > I had a few additional thoughts about the proposal: > > > > 1) Don't make the subpackage name itself directly visible when the > > enclosing package is "use"d -- just make the contents of the > > subpackage directly visible. > > > > I don't like this idea, but I don't feel strongly about it. Same here. >...I don't see any definitional problems with it - it just seems >strikes me as unnecessarily irregular. > I would be interested in knowing whether others feel that I am >underestimating the impact of the name space pollution problem, ... > > 2) Allow "use" in front of a package rename, with the effect of > > "open"ing the named package. ... > Is it clear that we need this generality? I won't say we "need" it, but it seems quite useful. > > 3) Consider using syntax similar to aspect specification > > to rename components as part of doing a "use package": ... > This seems like a fairly big change, because it is no longer so simple > to define this construct just in terms of use-visibility. Furthermore, it doesn't buy you much. What does this give you beyond: use package ADT_Vectors is new Vectors(ADT, ...) subtype ADT_Vector is ADT_Vectors.Vector, subtype ADT_Cursor is ADT_Vectors.Cursor; ? **************************************************************** From: Ed Schonberg Date: Thursday, March 12, 2009 4:25 PM > I think the namespace pollution issue is a big one. If we don't do > this, I foresee people using all kinds of weird names to try to be > sure they don't have any collisions, and that seems like a waste of > time and something that will make such packages look bogus. Hiding > the name of the subpackage itself from use visibility, for a > subpackage that is "used at birth" > (or "always open") feels like a much cleaner approach, and allows the > user to pick a name for the subpackage that is descriptive rather than > intentionally obscure. I don't see it as "irregular" > but rather I see it as a fairly natural consequence of having its > contents always use-visible. The potential for name pollution comes from the opening of the package, not from the name of the package on its own. If the name is in the source it should be usable, and it should still be possible to use qualified names to retrieve its contents (even if this is redundant). I agree with the other critics of this idea: a name in the source must be usable. Otherwise <> would be the right name, but this has been discarded for other reasons. **************************************************************** From: Randy Brukardt Date: Thursday, March 12, 2009 4:55 PM ... > > Incidentally, should the following be legal? > > > > package Outer is > > use package Inner is > > type T is new Integer; > > end Inner; > > end Outer; > > > > limited with Outer; > > package Snort is > > type T_Ref is access Outer.T; > > end Snort; > > > > In other words, is the distinction between a "use" and a "non-use" > > package retained in the limited view of an enclosing package? > > It would be weird if when you changed from non-limited to limited or > vice versa, you had to insert/remove the name of the subpackage. I > don't see a big implementation burden here, and thus far limited views > have been subsets of the full view. It would be annoying if they were > not just subsets, but really markedly different from the full view. Umm, normally use clauses aren't allowed for limited views. I forget the exact reasons, but I believe it had something to do with weird cases that we don't want to allow. So having use visibility of a limited view *solely* for this use would surely be unusual and irregular. OTOH, not having it would harm the abstraction that we're trying to create. I'm not sure what the correct answer is. **************************************************************** From: Steve Baird Date: Thursday, March 12, 2009 5:54 PM > I think the namespace pollution issue is a big one. As I said, I don't feel strongly about this one. If I really thought that it would be common practice to introduce weird names to avoid collisions, I would certainly support this proposal. It does seem odd that you would get an error if you move some code out of the enclosing package (code which references the inner package explicitly) and plunk it down within the scope of a use-clause which names the outer package. On the other hand, I suppose this is an unimportant corner case. What about the case where explicit qualification is needed to resolve a use-visibility collision? generic type T is private; package G is X : Integer; ... ; end G; with G; package P is use package I1 is new G (Integer); use package I2 is new G (Float); end P; with P; use P; package P_Client is Y : Integer renames I1.X; -- illegal Z : Integer renames P.I1.X; -- legal end P_Client; Don't you think users would find this confusing? >>> 2) Allow "use" in front of a package rename, with the effect > > Why are these pathological? Can you explain what makes them worse > than a rename followed by a normal "use" of the package? (My mind > isn't as fiendish as yours, I suspect.) >> You may be right. I ran into problems with these constructs during an earlier attempt at this problem, but I think the equivalences we have been discussing move these constructs from the "undefined" category into merely "confusing". Would this sort of thing pose any problems? package Outer is package Inner is use package Ren renames Outer; end Inner; X : Integer := ... ; Y : Integer := Inner.X -- legal + Inner.Y -- illegal + Inner.Z; -- illegal Z : Integer := ... ; end Outer; I remember very tangled discussions about the rules concerning a package which has a limited with of a unit but which has the full view of the unit indirectly in its closure. Would there be issues along those lines with something like package Pkg is Squamish_Players_Per_Team : constant := 43; end Pkg; limited with Pkg;; package Zoofle is use package Ren renames Pkg; end Zoofle; with Pkg; with Zoofle; package Doofle is X : Integer := Zoofle.Squamish_Players_Per_Team; -- legal? end Doofle; ? ... >> This feature also has a very high potential for misuse. >> Use clauses can already be used to write code which is difficult to >> read; this construct would make matters even worse in this respect. > > I'm not sure I see the issue here, so perhaps an example might help. I see this as the stronger argument against this construct. If you have something like use P1; use P2; use P3; ... use P9; package User is X : Integer := Foo; end User; and you have no idea where Foo is coming from, you currently have to look in each of the used specs. If any of the packages named in the use clauses are library unit renames, you have to look through those renames. Assuming that you haven't run into a particularly nasty case of an inherited function with defaults for all parameters, you ought to find it in one of those specs. If each of the used units now looks like with P3A; with P3B; ... with P3Z; package P3 is use package A renames P3A; use package B renames P3B; ... end P3; , you've got a bigger search on your hands. And it need not end there - P3A may have the same structure. And woe unto you if you're down in a subunit where you've inherited a with of the child unit P3B.Foo ... In the restricted case where "use package" is not used for renames, you can still see this search-space expansion in the case of an instance (now you have to go look at the generic spec) but somehow this seems more forgivable (for one thing, it stops after one iteration if you ignore library unit renames). >> >> Incidentally, should the following be legal? >> >> package Outer is >> use package Inner is >> type T is new Integer; >> end Inner; >> end Outer; >> >> limited with Outer; >> package Snort is >> type T_Ref is access Outer.T; >> end Snort; >> >> In other words, is the distinction between a "use" and a "non-use" >> package retained in the limited view of an enclosing package? > > It would be weird if when you changed from non-limited to limited or > vice versa, you had to insert/remove the name of the subpackage. I > don't see a big implementation burden here, and thus far limited views > have been subsets of the full view. It would be annoying if they were > not just subsets, but really markedly different from the full view. >> I completely agree. The argument against this (which I think is weak) is that this requires extracting additional information from the limited view before full semantic analysis has been run. ... >>> 3) Consider using syntax similar to aspect specification >>> to rename components as part of doing a "use package": ... >> This seems like a fairly big change, because it is no longer so >> simple to define this construct just in terms of use-visibility. > > That's why I said "consider using" ;-) > I think Bob makes a good point here. It is sufficiently easy to do this using existing mechanisms that the cost associated with inventing any sort of new mechanism does not seem to be justified (Bob - speak up if I'm misinterpreting your comment). Adding implicit declarations to a package at some point other than the package declaration is fine as long as you are sure that you are the only one doing it. Suppose that you add a subtype named Foo to a package spec which then turns out to have a child unit named Foo. We ran into this with the two different mechanisms for defining a child unit of a package instance. Not that this case couldn't be defined, but why bother? There might also be issues with a Foo declared in the private part of the package and a client who can see both the private part and the rename-inducing "use package". **************************************************************** From: Steve Baird Date: Thursday, March 12, 2009 6:13 PM > limited with Pkg; > package Zoofle is > use package Ren renames Pkg; > end Zoofle; > Randy Brukardt wrote: > Umm, normally use clauses aren't allowed for limited views. I suppose the rule Randy cited would need to be generalized to disallow the above example if "use" package renames are introduced. **************************************************************** From: Bob duff Date: Thursday, March 12, 2009 6:58 PM > > I think the namespace pollution issue is a big one. Tuck, I don't understand the problem here. Why wouldn't people just use some naming convention, like "use package Subpack is...", and have lots of things all over the place called Subpack, which are not directly nameable because they all conflict with each other? I don't see why anyone would write "use package Some_Horribly_Long_Name_That_Nobody_Would_Write". I mean, there's no real _harm_ in referring to it, so why not call them all "Subpackage" or whatever? Am I missing something obvious? We're talking about the package name polluting, not the contents of, right? If you like, you can call it "use package Box is" instead of "use package <> is". ;-) > As I said, I don't feel strongly about this one. Me, too. ... > , you've got a bigger search on your hands. And it need not end there > - P3A may have the same structure. And woe unto you if you're down in > a subunit where you've inherited a with of the child unit P3B.Foo ... I don't buy such "can be abused" arguments. Patient: It hurts when I abuse use. Doctor: So don't do that. ;-) Anyway, in practice, you click on your fancy IDE to find the thing. > > That's why I said "consider using" ;-) > > I think Bob makes a good point here. > It is sufficiently easy to do this using existing mechanisms that the > cost associated with inventing any sort of new mechanism does not seem > to be justified (Bob - speak up if I'm misinterpreting your comment). [Bob wisely remains silent, indicating that Steve understood correctly. ;-)] **************************************************************** From: Tucker Taft Date: Thursday, March 12, 2009 8:19 PM > The potential for name pollution comes from the opening of the > package, not from the name of the package on its own. If the name is > in the source it should be usable, and it should still be possible to > use qualified names to retrieve its contents (even if this is > redundant). I agree with the other critics of this idea: a name in the > source must be usable. Otherwise <> would be the right name, but this > has been discarded for other reasons. Just to be clear, I'm not suggesting the name completely disappears. I'm suggesting that if you "use" the *enclosing* package, and thereby also implicitly "use" the inner package, you don't end up with the name of the inner package itself directly (use) visible. For example: package P is type S is ...; use package Inner is type T is ... end P; -- At this point "Inner", "S", -- "Inner.T" and simply "T" are all legal names. end P; -- At this point, "P", "P.S", "P.Inner", "P.Inner.T", -- "P.T" are all legal names. use P; -- At this point, in addition to the above, we can -- now use "S" and "T" legally. My proposal is that -- "Inner" would *not* be directly use-visible, -- but you could still say "P.Inner". The reason I am recommending this is that I think without it you will have trouble using this feature when you are given an abstract spec, and you decide the best way to implement it is using one of these "used-at-birth" subpackages. For example, suppose we define a package spec in the Ada standard, or the ASIS standard. It turns out it can be nicely implemented by one or more used-at-birth package instantiations, package renames, or nested packages. Unfortunately, if someone does a "use" of this package, some user code might not compile which should compile, because of the "extra" identifiers introduced by these used-at-birth packages. To avoid this problem, the implementor will have to pick names that are unlikely to collide with any names used by clients, meaning that every conscientious user of this feature will find themselves using stupid-looking intentionally-unlikely names on all such used-at-birth packages. By simply saying that the name of a used-at-birth subpackage doesn't get made use-visible when the enclosing package is used, we eliminate this need to use bogus-looking names. **************************************************************** From: Jean-Pierre Rosen Date: Friday, March 13, 2009 3:30 AM > I think the namespace pollution issue is a big one. If we don't do > this, I foresee people using all kinds of weird names to try to be > sure they don't have any collisions, and that seems like a waste of > time and something that will make such packages look bogus. Hiding > the name of the subpackage itself from use visibility, for a > subpackage that is "used at birth" > (or "always open") feels like a much cleaner approach, and allows the > user to pick a name for the subpackage that is descriptive rather than > intentionally obscure. I don't see it as "irregular" > but rather I see it as a fairly natural consequence of having its > contents always use-visible. If you think the name of the package itself is not useful, why not make the package anonymous, as I suggested earlier? i.e. instead of "use package Blah is ", just say "package is", and such a package would automatically be "open". **************************************************************** From: Edmond Schonberg Date: Friday, March 13, 2009 8:44 AM Jean-Pierre's latest suggestion that the fictitious package should have no name brings us back to the beginning: replace "use package" with "private" and what we have is the multiple private parts proposal, with fewer characters. In fact this is the full semantic contents of the "use package" proposal: a way of slipping in interspersed visible and private parts. If this is the desired effect we might as well go with it rather than define strange semantics for "almost invisible" inner packages. The implementation costs might be even smaller. **************************************************************** From: Steve Baird Date: Friday, March 13, 2009 9:07 AM > If you think the name of the package itself is not useful, why not > make the package anonymous, as I suggested earlier? i.e. instead of > "use package Blah is ", just say "package is", and such a package > would automatically be "open". Someone pointed out that there are problems with providing the body for an anonymous package, particularly if you declare more than one in the same enclosing package. **************************************************************** From: Bob Duff Date: Friday, March 13, 2009 7:09 AM > For example, suppose we define a package spec in the Ada standard, or > the ASIS standard. It turns out it can be nicely implemented by one > or more used-at-birth package instantiations, package renames, or > nested packages. Unfortunately, if someone does a "use" of this > package, some user code might not compile which should compile, > because of the "extra" identifiers introduced by these used-at-birth > packages. To avoid this problem, the implementor will have to pick > names that are unlikely to collide with any names used by clients, > meaning that every conscientious user of this feature will find > themselves using stupid-looking intentionally-unlikely names on all > such used-at-birth packages. By simply saying that the name of a > used-at-birth subpackage doesn't get made use-visible when the > enclosing package is used, we eliminate this need to use bogus-looking > names. I find this argument convincing. In fact, in this scenario, even a stupid-looking name is technically in violation of the ASIS standard. Can you tell us what the problem is with your "use package <> is" idea? I assume the same issues would apply to J.P.'s proposed syntax "package is" (which seems preferable -- we got enough <>'s in the syntax!). **************************************************************** From: Tucker Taft Date: Friday, March 13, 2009 12:22 PM The problem with "package <> is" comes when you get to the body. If there is more than one, things are ambiguous. Also, Erhard made a convincing argument that if you use unnamed packages, there really is no good way to workaround collisions that might come up due to multiple "use" clauses. With a name for these packages, you can at least give a full expanded name in the case of a collision. **************************************************************** From: Tucker Taft Date: Friday, March 13, 2009 3:43 PM I just ran into another potential use for this feature. Periodically I want to define a new generic package which has somewhat different formal parameters than an existing generic, but declares essentially the same types and operations. This could be done quite easily with this proposed feature: given an existing generic: generic type T is private; with procedure Op(...); Blah : in T; package Old_Generic is type Container is private; ... end Old_Generic; Presume I want to create a version of the generic that only takes derivatives of P.T as the formal type, and has fewer generic formals: with P; generic type T is new P.T; package New_Generic is use package Instance is new Old_Generic(T, Op => P.Op1, Blah => T(P.Val)); end New_Generic; Very elegant, in my view. In the current language you end up having to repeat the entire package spec, and then in the private part or body instantiate the old generic, and define each of the operations via renaming or wrapping of the old one. (This is another example of where you really have no interest in having the name "Instance" be made directly visible when you apply a "use" clause to some instance of New_Generic, though I promise that wasn't the point of the example... ;-) **************************************************************** From: Steve Baird Date: Friday, March 13, 2009 4:14 PM > Very elegant, in my view. Typos aside, I agree. This could be a useful idiom. > In the current language you end up having to repeat the entire package > spec, and then in the private part or body instantiate the old > generic, and define each of the operations via renaming or wrapping of > the old one. > > (This is another example of where you really have no interest in > having the name "Instance" be made directly visible when you apply a > "use" clause to some instance of New_Generic, though I promise that > wasn't the point of the example... ;-) Until you want to instantiate a generic which takes a formal instance of Old_Generic. This is really more of an argument against making the name completely unavailable, as opposed to your proposal which only makes it unavailable via a use clause. **************************************************************** From: Bob Duff Date: Friday, March 13, 2009 4:56 PM > Very elegant, in my view. Yes, very nice. **************************************************************** From: Randy Brukardt Date: Thursday, March 19, 2009 12:25 AM ... > >> 2) Allow "use" in front of a package rename, with the effect of > >> "open"ing the named package. > >> ... > > > > Is it clear that we need this generality? > > > > This would require adding wording to cope with various pathological > > cases that this would make possible (e.g. a rename of a limited view > > of a package, a rename of a package whose spec encloses the rename). > > Why are these pathological? Can you explain what makes them worse > than a rename followed by a normal "use" of the package? (My mind > isn't as fiendish as yours, I suspect.) The problem I see here is that we decided we didn't like "use all pack;" because of issues with re-exporting existing packages (rather than new, as in an instance or nested package). We didn't like: with Bar; package Glarch is use all Bar; end Glarch; But Tucker wants to allow: with Bar; package Glarch is use package Foo renames Bar; end Glarch; I have an amazingly hard time figuring the (semantic) difference between these two, except that we now have a name Glarch.Foo. That doesn't seem to be an improvement over "use all Bar"! --- The annoying thing here is I didn't record in the minutes precisely why we thought this was distasteful. All I have is: There is some discomfort with the idea that this essentially forces a use clause into clients of a package. Use-adverse programmers will not be happy. It seems especially weird when it is used on a package that is defined outside of the current unit. Based on this, I'm confused. If we don't find the latter example distasteful, then I don't see how we can find the former example distasteful, either. So then we should simply go back to "use all Pack", because it surely looks simpler to me than this funny prefix. Otherwise, "use" should not be allowed on renames. **************************************************************** From: Tucker Taft Date: Thursday, March 19, 2009 7:48 AM One of Erhard's major complaints with the "use all" was that it was subject to use-clause cancellation with no workaround. The "use package Foo renames X.Bar;" avoids that problem, by providing the name "Foo" as a way to identify declarations within X.Bar that might be subject to cancellation. I don't understand the "use-averse" comment. The presumption is that if they didn't have this capability, the programmer would have to use a derived type or a bunch of individual renames to get the names from the other package declared directly within the new package. This merely allows the programmer to do this more efficiently and without the cut-and-paste errors that can crop up with a sequence of renames. It also avoids introducing the potentially confusing new types that a derived type would imply. Nothing is made directly visible unless the client of the enclosing package decides to "use" that package, which of course wouldn't happen if the client were use-averse. **************************************************************** From: Micronian Date: Thursday, April 2, 2009 12:38 PM I read this AI and while it does seem useful to make a package abstraction by "combining" other packages, I am concerned about the possibility of people inadvertently coding a hierarchy such as below: package Pkg1 is -- some stuff declared here end Pkg1; with Pkg1; package Pkg2 is use package Inner renames Pkg1; -- some stuff declared here end Pkg2; with Pkg2; package Pkg3 is use package Inner renames Pkg2; -- some stuff declared here end Pkg3; This would make it confusing for a person to trace down declarations since now you have to look through a chain of packages. This is also one of the reasons why I dislike working with C code with the transitive nature of the #include statements: header1.h <----header2.h <----header3.h This linear case is simple, but sometimes there are a few more branches of header dependencies further along the hierarchy. It is possible that this new extended-scope use clause would invite such coding habits into Ada (e.g. C programmers programming Ada in a C like fashion). Any thoughts? **************************************************************** From: Randy Brukardt Date: Thursday, April 2, 2009 1:44 PM This has recently been discussed within the ARG. The original "use package" proposal only applied to nested packages and to instances, but not renames. So the example you give wasn't intended. It was suggested that it be extended to include package renames for consistency reasons. But I think that is essentially the same as the original "use all ;" proposal, and the reason it was rejected (at least the reason *I* rejected it) was because of examples like the one you give here. "Use all ;" has the effect of allowing the author of an abstraction to *force* clients to use "use clauses" whether they want to or not. For programmers that are use-averse (like me), that's not acceptable. The non-use-adverse claim that the inclusion of the name in the renames makes all the difference, and I admit that it does help a bit - you at least have a fully expanded name to use. But the readability problems in other people's code remain. The argument has been made that you can use your fancy IDE to find the declaration; but I don't buy that. My "fancy IDE" is an 1980's vintage MS-DOS text editor, since it is a better *programming editor* than anything I've ever tried on Windows. No IDE capabilities there, and oddly enough, I don't expect an update to add any! And, in any case, you have to be able to compile most of a program before the fancy IDE can provide any help at all; it is no help on snippets of code or partially completed programs. **************************************************************** From: Micronian Date: Thursday, April 2, 2009 2:19 PM I'm glad to hear that renames were rejected. That helps keep the hierarchy very shallow. Normally when I use a USE clause in my Ada code, I still try to qualify many, but not all things with the package names because I dislike looking into code that makes it inconvenient to find out at first glance where things came from. Like you, I often still use my good ol' super "text" editor (Emacs :) ), but there are plenty of cases where the projects I'm on happen to use a nicer IDE (e.g. Visual Studio). While these IDE's make code navigation easier, I agree that it should not be a requirement to have these tools around. **************************************************************** From: Dmitry A. Kazakov Date: Thursday, April 2, 2009 2:15 PM > Any thoughts? I think that the proposal is extremely important for designing reusable components. Presently it is impossible to decompose a package declarative part into several smaller packages without exposing these parts later to the clients. Since their declarations remain outside the "main" package, the user must be aware of the relationship between these packages, which is often occasional and implementation controlled. This becomes especially painful with instances of generic packages within the declarative part. Another extremely important case is generic packages themselves. For example: generic with package Numbers is new Rational_Numbers (<>); package Rational_Series is use all Numbers; ... end Rational_Series; It makes no sense to do "use Rational_Series" without "use"-ing its numbers. Usually such hierarchies of packages are quite deep, and it is very tedious to find all corresponding instances and then "with" and "use" all of them. In my view transitiveness is exactly what is needed to make design of packages use-clause friendly. **************************************************************** From: Micronian Date: Thursday, April 2, 2009 2:33 PM Don't get me wrong, I still think the feature is useful, but I just don't think WITHing one package should automatically include a large deep hiearchy that makes it a hassle to navigate through. When I learned Ada, I was happy to see that it did not have the transitive nature of C's #includes. But, yes I have come into cases like you have said where you split the implementation to keep the "main" package smaller, yet increasing the number of WITHs for the user. I just think it would be great if the extended-use feature has a good balance. **************************************************************** From: Randy Brukardt Date: Thursday, April 2, 2009 11:06 PM > I'm glad to hear that renames were rejected. That helps keep the > hierarchy very shallow. Umm, that's my opinion; the full ARG hasn't really considered the issue. I could imagine it going either way, because this is a "deep pit" issue (as Tucker once described it). We're already in a deep pit (in this case, because of inheritance -- it's pretty much necessary to have a tool to figure out where things are declared when doing OOP, because nothing interesting appears in the source), and it is not clear whether there is any reason to avoid going all the way to the bottom. I know where *I* stand on this one, and it is good to know that others think like me. But whether that will be enough to defeat the forces of "consistency" remains to be seen. **************************************************************** From: Randy Brukardt Date: Thursday, April 23, 2009 12:22 AM Now for the technical comments: You have: Add after 8.4(11) (i.e. append to the end of the Static Semantics section) At any point where an integrated package or package renaming is either potentially use-visible or directly visible, and where an entity declared immediately within the package or renamed package is visible, the entity is potentially use-visible. This wording is very different than the existing wording for potentially use-visible (which talks about "scope enclosing a place" and "each declaration that occurs immediately within the declaration region of the package"). And we always try to avoid stuff like "at any point". You seem to have "solved" that by putting it as far away as possible from 8.4(8). But I think all of reasons for a declaration being "potentially use-visible" should go together; this paragraph should go right after 8.4(8) (it's not really related to "potentially use-visible by selection", which can go later). ---- The main reason I was reading the above was that I was trying to figure out what happens when the same declaration is use-visible (and use-visible by selection) by multiple paths. This is where the equivalence you give at the beginning of the discussion breaks down (or something is getting very complicated). Consider the following: package P1 is C : constant Boolean := True; end P1; with P1; package P2 is use package P3 renames P1; end P2; with P1, P2; use P1, P2; procedure M1 is begin if C then -- OK? ... end if; end M1; One would like this to be legal, by analogy with a regular use clause. But use-by-selection starts getting messy: with P1; package P4 is use package P1 renames Standard.P1; end P4; with P1, P4; use P1, P4; procedure M2 is begin if P1.C then -- (1) ... end if; end M2; Is (1) OK? I suppose it is because the prefix P1 is directly visible, which hides the use-visible P4.P1. But then try: with P1; package P5 is use package P1 renames Standard.P1; end P5; with P4, P5; use P4, P5; procedure M3 is begin if P1.C then -- (2) ... end if; end M3; Both P4.P1.C and P5.P1.C denote the same declaration, so one would expect this to work. (That is how I read the proposed wording.) But it isn't clear to me at all how that gets worked out from the non-overloadable prefixes. (Currently, there can only be one interpretation of a non-overloadable name, but here it seems that we have two.) One could argue that we don't need this complication, and declare (2) to be ambiguous. But then you would risk eliminating declarations that don't conflict with anything and that we need to see. package P6 is V : Integer := 0; end P6; with P6; package P7 is use package P1 renames Standard.P6; end P7; with P4, P7; use P4, P7; procedure M4 is begin if P1.C then -- (3) ... end if; end M4; One would hope there is no ambiguity here (in (3)), as there is only one C in the program. Again, this is what the wording appears to say. So it looks like *everything* is overloadable in a practical sense (the compiler will have to treat every prefix that way at least). At which point I wonder what the advantage of continuing to even have non-overloadable declarations will be, since they'll no longer provide any benefit in resolution or error correction. Unfortunately, this last case can happen with instances and nested packages as well as package renames (obviously, the declarations can't be same unless a rename is involved). I admit that I was trying to find cases where "integrated" renames caused semantic problems, but it appears that instead I've found that this whole idea is going to cause significant implementation problems (at least for any implementation that takes any advantage of knowing that a name is non-overloadable). This is going to happen a lot in practice, because there is no good name for typical nested packages (especially as there is no real intent to use them via that name). So I expect a lot of use of the same names (much like the style of calling all object-oriented types "Object" and using the package name as the "real" name). That's going to lead to this sort of conflict. So it appears that my objection to the renames case has now spread to the entire idea. Besides making it pretty much impossible to find the declaration of something without a fancy tool (which of course doesn't work if the code isn't compilable), it also will substantially complicate implementation. So, if this is the proposal that is going forward, I'm opposed. **************************************************************** From: Steve Baird Date: Thursday, April 23, 2009 10:37 AM > ... this paragraph should go right after 8.4(8) (it's not really > related to "potentially use-visible by selection", which can go > later). > Good point. > ---- > > The main reason I was reading the above was that I was trying to > figure out what happens when the same declaration is use-visible (and > use-visible by > selection) by multiple paths. This is where the equivalence you give > at the beginning of the discussion breaks down (or something is > getting very complicated). Consider the following: > > package P1 is > C : constant Boolean := True; > end P1; > > with P1; > package P2 is > use package P3 renames P1; > end P2; > > with P1, P2; use P1, P2; > procedure M1 is > begin > if C then -- OK? > ... > end if; > end M1; > > One would like this to be legal, by analogy with a regular use clause. And we agree that it is legal, right? > But use-by-selection starts getting messy: > > with P1; > package P4 is > use package P1 renames Standard.P1; > end P4; > > with P1, P4; use P1, P4; > procedure M2 is > begin > if P1.C then -- (1) > ... > end if; > end M2; > > Is (1) OK? I suppose it is because the prefix P1 is directly visible, > which hides the use-visible P4.P1. > Right. > But then try: > > with P1; > package P5 is > use package P1 renames Standard.P1; > end P5; > > with P4, P5; use P4, P5; > procedure M3 is > begin > if P1.C then -- (2) > ... > end if; > end M3; > > Both P4.P1.C and P5.P1.C denote the same declaration, so one would > expect this to work. (That is how I read the proposed wording.) But it > isn't clear to me at all how that gets worked out from the non-overloadable prefixes. > (Currently, there can only be one interpretation of a non-overloadable > name, but here it seems that we have two.) > This is ambiguous because the prefix of the name P1.C is ambiguous. I believe the wording I suggested captures this correctly, and that this is consistent with the informal implicitly-inserted-use_clauses model. It is similar to the following situation in the current language: declare package P1 is X : Integer := 0; end P1; package P2 is package P1_Ren renames P1; end P2; package P3 is package P1_Ren renames P1; end P3; use P2, P3; Y : Integer := P1_Ren.X; -- illegal > One could argue that we don't need this complication, and declare (2) > to be ambiguous. But then you would risk eliminating declarations that > don't conflict with anything and that we need to see. > > package P6 is > V : Integer := 0; > end P6; > > with P6; > package P7 is > use package P1 renames Standard.P6; > end P7; > > with P4, P7; use P4, P7; > procedure M4 is > begin > if P1.C then -- (3) > ... > end if; > end M4; > > One would hope there is no ambiguity here (in (3)), as there is only > one C in the program. Again, this is what the wording appears to say. > No, I believe the prefix of the name "P1.C" is ambiguous and that this example should be rejected. Certainly that was my intent and I believe the suggested wording captures that. > So it looks like *everything* is overloadable in a practical sense > (the compiler will have to treat every prefix that way at least). At > which point I wonder what the advantage of continuing to even have > non-overloadable declarations will be, since they'll no longer provide > any benefit in resolution or error correction. I don't think so. I think the bad consequences you describe are based on a misunderstanding. Support for ambiguous prefixes was certainly not intended. I believe the suggested wording captures that intent, but if it doesn't then the wording needs to be corrected. **************************************************************** From: Tucker Taft Sent: Thursday, May 21, 2009 5:27 AM I had a couple of "waking up" thoughts, as Jimmy Carter used to call them: [Editor's note: The other thought pertains to AI05-0142-2 and is filed in that AI.] 2) I had a perfect example of why the name of an "integrated" package should not become "use" visible when the enclosing package is "use"d, and a good example of why integrated package renames are useful: Text_IO, Sequential_IO, and Direct_IO each include renames of each exception declared in the package IO_Exceptions. It is a no-brainer to replace those renames with a: "use package renames Ada.IO_Exceptions;" in Text_IO, Sequential_IO, and Direct_IO. However, what should the be? Well, most natural would be: "use package IO_Exceptions renames Ada.IO_Exceptions;" but now suppose you have: with Ada.IO_Exceptions; with Ada.Text_IO; use Ada; procedure What_Fun is use Text_IO; -- oops, IO_Exceptions suddenly disappears begin ... exception when IO_Exceptions.Name_Error => -- oops, this doesn't compile any more! end What_Fun; There is always a compatibility problem whenever we consider adding another identifier to an existing package. But the problem is always about "use" visibility of that new identifier. We don't worry about there being another identifier available for "selective" visibility, because no existing program would have a reference to the newly added identifier as a component of the existing package. I really think this integrated package concept will be a useful way to implement packages, allowing after-the-fact reusability of parts of packages, but every time I think about it, I end up worrying what the heck I am going to use for the names of those integrated packages. We considered allowing "<>" for the name of the integrated package, but Erhard seems to have convinced us that you need them for resolving ambiguities. Just making integrated package names *not* themselves use-visible provides an elegant solution and ensures upward compatibility when you decide to (re)implement part of the functionality of an existing package by using one. By the way, a *nice* feature of implementing the sequence of renames of exceptions via "use package blah renames Ada.IO_Exceptions" is that the exception names would presumably *not* cancel each other out if you use'd two different instantiations of, say, Direct_IO. But I suppose that depends on the details of the wording for how the contents of an integrated package rename (presuming we support them) become visible... Steve? **************************************************************** From: Randy Brukardt Sent: Friday, May 22, 2009 12:45 PM ... > There is always a compatibility problem whenever we consider > adding another identifier to an existing package. But the > problem is always about "use" visibility of that new identifier. > We don't worry about there being another identifier available > for "selective" visibility, because no existing program would > have a reference to the newly added identifier as a component > of the existing package. Client use clauses are always a problem. They ought to be used very sparingly. > I really think this integrated package concept will be a > useful way to implement packages, allowing after-the-fact > reusability of parts of packages, but every time I think > about it, I end up worrying what the heck I am going to use for > the names of those integrated packages. We considered allowing "<>" > for the name of the integrated package, but Erhard seems to have > convinced us that you need them for resolving ambiguities. You also need a name for limited with, which does not allow any use clauses. That has to be true for these "integrated" packages as well. (You could easily get the same sorts of anomolies.) > Just making integrated package names *not* themselves use-visible provides > an elegant solution and ensures upward compatibility when you > decide to (re)implement part of the functionality of an existing > package by using one. Banning client use clauses would work, too. ;-) > By the way, a *nice* feature of implementing the sequence of > renames of exceptions via "use package blah renames Ada.IO_Exceptions" > is that the exception names would presumably *not* cancel each other > out if you use'd two different instantiations of, say, Direct_IO. > But I suppose that depends on the details of the wording for > how the contents of an integrated package rename (presuming we > support them) become visible... Steve? I made an argument like this once (in terms of implementation difficulty) and Steve convinced me that there is none because we're not allowing anything new. This sounds new, since we get such conflicts from renames with client use clauses: package P is Obj : Natural; end P; with P; package Q is package Inner renames P; end Q; with P; package R is package Inner renames P; end R; with Q, R; procedure Test is use Q.Inner, R.Inner; begin Obj := 10; -- Not use-visible due to collision. end Test; I don't see why we would want "integrated packages" to work differently, that would make a lot of additional work and would add confusion to users (which kind of use clause works how??). **************************************************************** From: Steve Baird Sent: Friday, May 22, 2009 4:04 PM ... > However, what should the be? Well, most natural would be: > "use package IO_Exceptions renames Ada.IO_Exceptions;" > > but now suppose you have: > > with Ada.IO_Exceptions; > with Ada.Text_IO; > use Ada; > procedure What_Fun is > use Text_IO; > -- oops, IO_Exceptions suddenly disappears > begin > ... > exception > when IO_Exceptions.Name_Error => > -- oops, this doesn't compile any more! > > end What_Fun; > Good point. I'll certainly include this example in the AI. > By the way, a *nice* feature of implementing the sequence of > renames of exceptions via "use package blah renames Ada.IO_Exceptions" > is that the exception names would presumably *not* cancel each other > out if you use'd two different instantiations of, say, Direct_IO. > But I suppose that depends on the details of the wording for > how the contents of an integrated package rename (presuming we > support them) become visible... Steve? > Right. The compilation error you mentioned above can be fixed by replacing when Io_Exceptions.Name_Error => with when Name_Error => . **************************************************************** From: Tucker Taft Sent: Friday, May 22, 2009 4:18 PM > Right. Can you be a bit more specific? Randy seemed to think you had said something else. My presumption is that if we support integrated renames, "use"ing the package enclosing the integrated rename as well as "use"ing the renamed inner package itself will not cause cancellation, any more than it would if you do a "use" of two different renames of the same package. **************************************************************** From: Steve Baird Sent: Friday, May 22, 2009 5:22 PM I agree with your interpretation. One way to verify this is a two-step process 1) Check the informal definition of how integrated packages work at the beginning of the !discussion section to see if it yields the desired behavior. 2) Check the wording to see if it correctly captures the informal definition. We are looking at something like with Ada.Io_Exceptions; package P1 is use package Io_Exceptions renames Ada.Io_Exceptions; end P1; with Ada.Io_Exceptions; package P2 is use package Io_Exceptions renames Ada.Io_Exceptions; end P2; with P1, P2; use P1, P2; procedure Foo is begin ... exception when Name_Error => ... ; end Foo; By informal equivalence #2, "use P1" is equivalent to "use P1; use P1.Io_Exceptions". Ditto for "use P2". It has been well established since Ada83 that it makes no difference whether a use clause names a package or a rename of that package. It is similarly well established that being within the scope of two uses clauses of a given package is no different than being within the scope of one. Let me know if you would like a more detailed justification for thoee assertions, but this is stuff that hasn't changed in years. Taken together, this means that the use of Name_Error occurs in a use-clause state that is equivalent to being in the scope of a use clause that denotes Ada.Io_Exceptions. Thus, the use of the simple name Name_Error is legal. The AI wording that corresponds to equivalence #2 is At any point where an integrated package or package renaming is either potentially use-visible or directly visible, and where an entity declared immediately within the package or renamed package is visible, the entity is potentially use-visible. Note that homographs can be simultaneously potentially use-visible. The fact that they are homographs doesn't matter until we come to the question of whether they are use-visible (as opposed to potentially use-visible). **************************************************************** From: Tucker Taft Sent: Friday, May 22, 2009 2:12 PM > with Q, R; > procedure Test is > use Q.Inner, R.Inner; > begin > Obj := 10; -- Not use-visible due to collision. Huh? I don't believe that. Are you sure? Our front end has no problem compiling this example. > end Test; > > I don't see why we would want "integrated packages" to work > differently, that would make a lot of additional work and would add > confusion to users (which kind of use clause works how??). I agree that integrated renames should work like regular renames from this point of view, and my claim is that the above "use" clauses do *not* cause cancellation, because they are "using" the *same* package (through two different renamings). If you look in RM 8.4, it talks about *packages* denoted by the name appearing in the "use" clause (as opposed to "package declarations). A package *rename* does not create a distinct package, though of course it is a distinct declaration. In general when talking about entities (as opposed to declarations), renames are irrelevant. No matter how many different renames are involved, there is only one entity involved, and the two "use" clauses in your example denote the *same* package entity. So that is why I wanted integrated packages to work the same way. Steve, do you disagree? **************************************************************** From: Steve Baird Sent: Friday, May 22, 2009 5:44 PM I agree, both with respect to the existing language and with respect to integrated packages. **************************************************************** From: Steve Baird Sent: Friday, May 22, 2009 4:56 PM > I made an argument like this once (in terms of implementation > difficulty) and Steve convinced me that there is none because we're > not allowing anything new. This sounds new, since we get such > conflicts from renames with client use clauses: > > package P is > Obj : Natural; > end P; > > with P; > package Q is > package Inner renames P; > end Q; > > with P; > package R is > package Inner renames P; > end R; > > with Q, R; > procedure Test is > use Q.Inner, R.Inner; > begin > Obj := 10; -- Not use-visible due to collision. > end Test; I'm not sure what you are talking about here. This example is legal Ada. If you rewrote it with integrated package renames in what seems to me like the obvious way, it would still be legal. **************************************************************** From: Randy Brukardt Sent: Friday, May 22, 2009 5:29 PM Without Tucker's explanation, I would have no idea what you are talking about. Even with Tucker's explanation, I find the idea that this is supposed to work highly distrubing. I don't buy the notion that Q.Inner.Obj and R.Inner.Obj are the same entity for resolution proposes; you're essentially claiming that the full name of Obj in this example would be P.Obj, even though that name isn't even visible in this context. I suppose the counter-argument is that it doesn't matter to the Ada program (but it makes writing tools - especially a use-clause remover - much more difficult). What you guys are telling me is that the features which I detest so much about *this* proposal and its interaction with renames (to the point that I've been trying to keep this particular combination out of "integrated packages") is in fact already a part of Ada. Indeed, this entire area makes me so uncomfortable that at this point I cannot in good conscience support any expansion of use-visibility. (I'd rather see pragma Restrictions(No_Package_Use); added to the language...) Thus "integrated packages" are a dead idea in my mind, and I'm not going to spend any more of my time (other than administratively, of course) on it. P.S. The only acceptable way IMHO to deal with these interactions is to outright ban the use of 'package use' on packages that contains "integrated packages". But that seems like a weird restriction, even to me. P.P.S. Besides this issue, I've also soured on this version of "integrated packages" because they re-emerge annoyingly: you have to use the nested package name when you are using limited withs; you have to use the nested package name to work around naming conflicts; and I think there was another case where you had to use the "real" name (which I've forgotten the details of). This really doesn't have the effect of "integrating" the package (treating it as being non-nested for almost all purposes) as we really wanted. **************************************************************** From: Tucker Taft Sent: Friday, May 22, 2009 9:27 PM This is not very radical. In fact, when it doesn't work it is more surprising. Use-clause cancellation is generally a pain, and we should minimize unnecessary cancellation. **************************************************************** From: Randy Brukardt Sent: Friday, May 22, 2009 10:57 PM Ada is a name-equivalence language, and Q.Inner.Obj and R.Inner.Obj are not name equivalent; therefore they're not the same. They *ought* to use-clause cancel. I realize that I'm going against Ichbiah on this one, but I really don't want to see any expansion of what I see as a major error in the language definition. I really dislike using renames in this way in any case (the only reason to use a renames of a package is to provide a shorter name when you are *not* using use clauses; a use clause of such a package is clearly a hack and very bad style). Also note that I think that we have usage problems with this proposal anyway - above and beyond this particular concern. These nested packages are going to pop up at inopportune times; they are not really going to "feel" integrated. I'd really rather have a mechanism where the names really are part of the root package. (I realize that we sort of tried that initially and had some problems with collisions; I'm thinking now that such names should simply be illegal (rather than some sort of hiding)). But I've got so many other proposals to work on - ones that appear to work out - that spending more time on this one just doesn't make sense. P.S. This is all your fault. :-) I have such strong regrets about ignoring my gut feelings in favor of "rationality" and letting you put the "coextension" abomination into the language, that I've vowed to take stronger stands against things that "feel" wrong. This appears to be the first real test... **************************************************************** From: Randy Brukardt Sent: Friday, May 22, 2009 11:28 PM I should also say that if this is the way things are intended to work, it is extremely irregular in that it only works for packages. It greatly rubs me the wrong way that you would get different results for: with Ada.IO_Exceptions; package Ada.Text_IO is Name_Error : renames Ada.IO_Exceptions.Name_Error; end Ada.Text_IO; with Ada.IO_Exceptions; package Ada.Stream_IO is Name_Error : renames Ada.IO_Exceptions.Name_Error; end Ada.Stream_IO; with Ada.Text_IO, Ada.Stream_IO; use Ada.Text_IO, Ada.Stream_IO; -- This is pretty much the only time I'd use one of these... procedure Test is null; exception when Name_Error => null; -- Illegal, use-clause cancelation end; versus with Ada.IO_Exceptions; package Ada.Text_IO is use package IO_Exceptions renames Ada.IO_Exceptions; end Ada.Text_IO; with Ada.IO_Exceptions; package Ada.Stream_IO is use package IO_Exceptions renames Ada.IO_Exceptions; end Ada.Stream_IO; with Ada.Text_IO, Ada.Stream_IO; use Ada.Text_IO, Ada.Stream_IO; -- This is pretty much the only time I'd use one of these... procedure Test is null; exception when Name_Error => null; -- Supposedly OK. end; I want "integrated packages" to work exactly as if the declarations are in the outer package, and this difference is aggravating. **************************************************************** From: Tucker Taft Sent: Saturday, May 23, 2009 10:29 AM It doesn't work differently for package *names*, but it works for the contents of a package if it is "use"d via two different renames. Ada is not a "name equivalent" language in the sense you imply. It is "name equivalent" only if there are no renamings in the name, and you start from "Standard". Even "full conformance" allows matching of two different "expanded names" so long as they denote the "same" declaration (see 6.3.1(21.a-f) for an explicit discussion of this). Renaming a package doesn't create a new package, nor a new set of declarations for the components of the package. It only creates a new "package renaming declaration," and (somewhat annoyingly) these package *renamings* will cancel each other out in a case like: with R; package P is package S renames R.S; ... end P; with R; package Q is package S renames R.S; ... end Q; with P, Q; procedure Test is use P, Q; -- "S" is canceled out here; -- packages are *not* a special case use P.S, Q.S; -- But no cancellation occurs here. -- because P.S and Q.S denote -- the *same* package. Package renamings would not work if they created a new set of declarations of the components of the package. We clearly want P.S.T and Q.S.T to denote the same declaration. If for example, T is a type declaration, P.S.T and Q.S.T are the same type. **************************************************************** From: Edmond Schonberg Sent: Saturday, May 23, 2009 12:38 PM > with Ada.IO_Exceptions; > package Ada.Text_IO is > use package IO_Exceptions renames Ada.IO_Exceptions; > end Ada.Text_IO; > > with Ada.IO_Exceptions; > package Ada.Stream_IO is > use package IO_Exceptions renames Ada.IO_Exceptions; > end Ada.Stream_IO; > > with Ada.Text_IO, Ada.Stream_IO; > use Ada.Text_IO, Ada.Stream_IO; -- This is pretty much the only time I'd use one of these... > procedure Test is > null; > exception > when Name_Error => null; -- Supposedly OK. > end; > > I want "integrated packages" to work exactly as if the declarations > are in the outer package, and this difference is aggravating. But as Steve points out, this has always been legal with regular use- clauses on renamings. My understanding is that use clauses look through the renaming, and redundant use clauses are noops. If instead of "integrated packages" you write: with Ada.Text_IO, Ada.Stream_IO; use Ada.Text_IO, Ada.Stream_IO; -- This is the only time I'd use one of these ... procedure Test is use Ada.Text_IO.IO_Exceptions; use Ada.Stream_IO.IO_Exceptions; begin null; exception when Name_Error => null; -- Supposedly OK. end; this has been legal for some 14 years. **************************************************************** From: Randy Brukardt Sent: Tuesday, May 26, 2009 6:38 PM > It doesn't work differently for package *names*, but it works for the > contents of a package if it is "use"d via two different renames. Ada > is not a "name equivalent" language in the sense you imply. It is > "name equivalent" only if there are no renamings in the name, and you > start from "Standard". Sorry, but this last sentence is clearly False. And that is what I'm objecting to. If you had said: It is "name equivalent" only if there are no renamings {of packages} in the name, and you start from "Standard". then you would have made a True statement. But why should packages get this special treatment, and not renames of exceptions, or generics, or subprograms, etc.? We have "Ada is a name equivalence language unless the name is a renaming of a package". ... > with P, Q; > procedure Test is > use P, Q; -- "S" is canceled out here; > -- packages are *not* a special case > > use P.S, Q.S; -- But no cancellation occurs here. > -- because P.S and Q.S denote > -- the *same* package. > > Package renamings would not work if they created a new set of > declarations of the components of the package. > We clearly want P.S.T and Q.S.T to denote the same declaration. If > for example, T is a type declaration, P.S.T and Q.S.T are the same > type. I can't think of any legitimate reason to write a use clause of a renamed package in Ada 95 or 2005, so why do we care what happens here? In particular, why should it work differently than all other renames? Surely the packages could be different while still denoting the same underlying entities: that certainly works for renaming of other entities, such as exceptions. Nobody has made any effort to address (or even acknowledge) my other concerns. I'll put them on the main ARG list for further discussion. **************************************************************** From: Tucker Taft Sent: Tuesday, May 26, 2009 8:08 PM >> It doesn't work differently for package *names*, but it works for the >> contents of a package if it is "use"d via two different renames. Ada >> is not a "name equivalent" language in the sense you imply. It is >> "name equivalent" only if there are no renamings in the name, and you >> start from "Standard". > > Sorry, but this last sentence is clearly False. And that is what I'm > objecting to. If you had said: > > It is "name equivalent" only if there are no renamings {of packages} > in the name, and you start from "Standard". > > then you would have made a True statement. But why should packages get > this special treatment, and not renames of exceptions, or generics, or > subprograms, etc.? We have "Ada is a name equivalence language unless > the name is a renaming of a package". I am really confused by your world view on this one. I suspect someone else will have to pick up the discussion. But let me try one more time. Clearly renaming doesn't create a new entity, though it does represent a new declaration, and a new view, but of the same entity. This is independent of whether it is a package or any other kind of entity that allows renaming. Use-package clauses name a package *entity*, and it doesn't matter through which renaming they do so. Use-package clauses make certain *declarations* directly visible, those are the declarations that appear in the visible part of the use'd package entity. The declarations affected might be package (renaming) declarations, or any other kind of declaration, and they are all treated similarly -- package declarations aren't a special case. The *only* special case of which I am aware relative to "use" clauses is for the package "entity" that is named in the use clause. That seems as it should be. Now we can debate how this integrated package thing works, but I thought our model was that use'ing an outer package effectively does "use"s of all of the nested integrated packages. I have also proposed that the integrated package declarations are *not* made directly visible by "use"ing the enclosing package, sort of "in exchange" for their contents being made directly visible. This latter part is clearly up for debate, but doesn't seem related to your issue of "name equivalence." By the way, I wouldn't say Ada has "name equivalence," but rather it has "declaration equivalence." That is, it really doesn't matter how you get to the declaration, but so long as two names denote the same declaration, directly or through a rename, you are talking about the same thing. Anyway, I get the sense I am missing the points you have been making, so I'll let someone else take up the discussion from here if you think I am still way off base. **************************************************************** From: Randy Brukardt Sent: Wednesday, May 27, 2009 8:08 PM ... > I am really confused by your world view on this one. > I suspect someone else will have to pick up the discussion. I'm just trying to figure out why some renames are different than others wrt use clauses. Apparently the only justification is "that's just the way it is". It doesn't pay to argue about it further, simply because we aren't likely to make any changes in this area, and surely we're not going to get more restrictive. > But let me try one more time. Clearly renaming doesn't > create a new entity, though it does represent a new > declaration, and a new view, but of the same entity. > This is independent of whether it is a package or any other > kind of entity that allows renaming. Correct. > Use-package clauses name a package *entity*, and it doesn't > matter through which renaming they do so. Use-package > clauses make certain *declarations* directly visible, those > are the declarations that appear in the visible part of the > use'd package entity. The declarations affected might be > package (renaming) declarations, or any other kind of > declaration, and they are all treated similarly -- package > declarations aren't a special case. You're saying that use clauses work on entities (not declarations), but they make declarations (not entities) visible. Yeah, *that's* consistent. It's clear to me from trying to make sense of the ASIS semantic subsystem that any rules based on declarations alone make no sense at all -- because Ada has lots of anonymous items that don't have declarations (well, I suppose they're part of some unrelated declaration, but that's more confusing than helpful) -- and it has ways for entities to have multiple declarations. That way lies madness - and apparently that's where we are. (We'll get an opportunity to go into gory detail on the ASIS stuff, but not today...) > The *only* special case of which I am aware relative to "use" > clauses is for the package "entity" that is named in the use > clause. That seems as it should be. That's either too many or too few. ... > By the way, I wouldn't say Ada has "name equivalence," > but rather it has "declaration equivalence." That is, it > really doesn't matter how you get to the declaration, but so > long as two names denote the same declaration, directly or > through a rename, you are talking about the same thing. As I noted above, that way lies madness. I'm not sure if I'm the one that has gone mad (with Jean-Pierre's help vis-a-vis ASIS), but surely *someone* is insane here! Getting back to the original point: to say use-clause cancelation happens all of the time unless two package renames are involved makes no sense whatsoever. Either the cancelation should be canceled :-) whenever the same entity is referenced, or never. I'd prefer the latter, but the former is more compatible. > Anyway, I get the sense I am missing the points you have been > making, so I'll let someone else take up the discussion from > here if you think I am still way off base. As I said, it doesn't really matter at this point. This is clearly an insanity inherited from Ada 83, and never thought enough of to fix (unlike most of the other insanities of Ada 83). Doesn't pay to get too worked up over it, so I'm happy to agree to disagree (although I've still against further expansion of these bugs, so in the absence of a fix I'm not voting for any new forms of use clause). **************************************************************** From: Steve Baird Sent: Tuesday, May 26, 2009 1:27 PM Here are the updates to the AI that were discussed during the 5/20 call. [This is version /04 of the AI - Editor.] **************************************************************** From: Tucker Taft Sent: Tuesday, May 26, 2009 2:15 PM I had intended that the use-visibility rule would apply to *all* integrated packages, not just integrated package renames, since the same problem comes up when you declare an integrated subpackage, or an integrated generic instance. You really don't want the names of the integrated packages cluttering up the namespace when you do a "use" of the enclosing package, but you *do* want them when trying to disambiguate where you would be using a complete expanded name. **************************************************************** From: Steve Baird Sent: Tuesday, May 26, 2009 2:42 PM Tuck - Understood. Does the correction below fix the problem? Randy - here's a suggested update to the update (pending Tuck's ok): 1) Replace "Tuck suggests that the name of an integrated package rename should not itself become ..." with "Tuck suggests that the name of an integrated package (whether a rename or not) should not itself become ... " 2) Add the following before the paragraph that begins "As an aside ,,,": Tuck observes that this rule should "apply to *all* integrated packages, not just integrated package renames, since the same problem comes up when you declare an integrated subpackage, or an integrated generic instance. You really don't want the names of the integrated packages cluttering up the namespace when you do a "use" of the enclosing package, but you *do* want them when trying to disambiguate where you would be using a complete expanded name." If there is general agreement on this point, then the two alternatives described below should be discarded because they only address the case of an integrated package rename. [This was done in version /05 - Editor.] **************************************************************** From: Tucker Taft Sent: Tuesday, May 26, 2009 2:55 PM That's fine. **************************************************************** From: Randy Brukardt Sent: Tuesday, May 26, 2009 7:10 PM > Here are the updates to the AI that were discussed during the 5/20 call. You give the following example: > Integrated package renames also make possible a number of odd corner cases: > > limited with Foo; > package P is > use package Bar renames Foo; > end P; One would hope that this is illegal, because we don't allow any other "use" clauses on limited views of packages. I don't see any reason whatsoever for this to be different. (The problems shown in this example illustrate why that's the case.) ---- You give as an example: package Pkg is type T is tagged ... ; use package Inner is function Not_Primitive (X : T) return T; end Inner; end Pkg; and mention that you would like to be able to make the name Not_Primitive visible directly in Pkg. But this is exactly one of the sorts of effects that bothers me about this proposal. Clients cannot ignore the presence of the nested package, because its effects re-emerge periodically. This is one of those cases: the primitiveness changes, so for the purposes of deriving a type from T, you have to know the *real* structure of the package. ---- Similarly, consider another example from the AI: with Some_Container_Generic; package Pkg is use package Inner is type T is private; private ...; end Inner; use package I is new Some_Container_Generic (T); end Pkg; if you have a limited with of Pkg: limited with Pkg; procedure Main is Obj : access Pkg.T; -- Illegal! Obj2 : access Pkg.Inner.T; -- Yuck. begin the supposedly "integrated" package reappears. This also happens when there are use-conflicts, even if those conflicts are within a single package specification. ---- You can also name the inner package in a use-clause, giving visibility to only part of the supposedly "integrated" whole. This seems bad from the view of a client, which really wants to see a real, flat view of the package. Part of the trouble here is the name of this construct: there is nothing really "integrated" about these packages. It's really a way to force a use-clause on a client. If they were really "integrated", we would see none of the unusual effects seen here. ---- I realize that some differences from a fully flat package are going to be required (else you could just declare everything without the nested package). But some of these just seem outright wrong. I have to wonder if we can do better. I don't recall anymore why we switched to use-visibility for these things; it just seems wrong for many reasons. Perhaps there is some way to avoid that?? **************************************************************** From: Steve Baird Sent: Wednesday, May 27, 2009 11:49 AM ... > One would hope that this is illegal, because we don't allow any other "use" > clauses on limited views of packages. I don't see any reason > whatsoever for this to be different. (The problems shown in this > example illustrate why that's the case.) > It's not clear what this construct means, and that's the point (in the context of discussing whether integrated package renames are desirable). I agree that this probably should be illegal, but that doesn't fall out from the current wording - an explicit rule would be needed. > ---- > > You give as an example: > > package Pkg is > type T is tagged ... ; > > use package Inner is > function Not_Primitive (X : T) return T; > end Inner; > end Pkg; > > and mention that you would like to be able to make the name > Not_Primitive visible directly in Pkg. But this is exactly one of the > sorts of effects that bothers me about this proposal. Clients cannot > ignore the presence of the nested package, because its effects > re-emerge periodically. This is one of those cases: the primitiveness > changes, so for the purposes of deriving a type from T, you have to know the *real* > structure of the package. > I agree, although I should point out that this example came up in a "real" context; there was a serious question about how to accomplish this effect (an implementation-defined Not_Primitive pragma was mentioned as a possible solution). Nobody is pretending that this construct produces identical results to doing the "integration" by hand (although it is interesting to note that trying to rewrite this particular example without using this new construct gets a little messy - I think a subtype declaration is required). > ---- > > Similarly, consider another example from the AI: > > with Some_Container_Generic; > package Pkg is > use package Inner is > type T is private; > private > ...; > end Inner; > > use package I is new Some_Container_Generic (T); > end Pkg; > > if you have a limited with of Pkg: > > limited with Pkg; > procedure Main is > Obj : access Pkg.T; -- Illegal! > Obj2 : access Pkg.Inner.T; -- Yuck. > begin > > the supposedly "integrated" package reappears. > I think the problems with limited views of integrated packages and package renames are dwarfed to the point of insignificance by the existing treatment of limited views of instantiations and of ordinary package renames (which I am not arguing against). Limited views (technical term) provide a very limited view (common English) of things. That's ok, we've accepted that. As the AI mentions, we do have the option of relaxing this limitation and allowing a limited view of an integrated package to be integrated in some cases (not for renames, not for instances). > This also happens when there are use-conflicts, even if those > conflicts are within a single package specification. > > ---- > > You can also name the inner package in a use-clause, giving visibility > to only part of the supposedly "integrated" whole. This seems bad from > the view of a client, which really wants to see a real, flat view of the package. The point is that the client has the option of seeing a flat view of the package. If the client wants to explicitly disambiguate a name by referencing the inner package, that's up to the client. If you want to disallow this, it could, I suppose, be detected by some coding-standard-enforcement tool; that is outside of the scope of the language definition. > Part of the trouble here is the name of this construct: there is > nothing really "integrated" about these packages. It's really a way to > force a use-clause on a client. If they were really "integrated", we > would see none of the unusual effects seen here. This is a relatively lightweight solution. The fact that it is based on use-clauses definitely does show through in some cases. On the other hand, if this solves the "exporting a private type and a container thereof" problem (and I think it does), then that alone is a strong argument for including this feature. > ---- > > I realize that some differences from a fully flat package are going to > be required (else you could just declare everything without the nested > package). But some of these just seem outright wrong. > > I have to wonder if we can do better. I don't recall anymore why we > switched to use-visibility for these things; it just seems wrong for many reasons. > Perhaps there is some way to avoid that?? It's often true that as we come to see the problems with one approach in more detail, the path that we didn't take starts looking more and more attractive. As you noted, the early discussions of this AI were based on implicit rename/subtype declarations and had nothing to do with use clauses. One problem with that approach was collisions. Implicit declarations of non-overloadable constructs would have taken some work to straighten out. Use-visibility provided a reasonable approach to resolving these issues that was already a well-defined part of the language, If you think we should revive the rename/subtype approach or even pursue an entirely new solution, either a) provide a reasonably specific alternative that we can discuss or b) get more of a consensus from the group that we want to at least explore some other alternative and then I'll try to write something up. **************************************************************** From: Randy Brukardt Sent: Wednesday, May 27, 2009 9:09 PM ... > It's not clear what this construct means, and that's the point (in the > context of discussing whether integrated package renames are desirable). > I agree that this probably should be illegal, but that doesn't fall > out from the current wording - an explicit rule would be needed. Yes, of course such a rule is needed. I've been saying that repeatedly (see my mail of December 18th, 2008 and March 12, 2009), and you've never added the rule to the actual AI. Please do so (or at least explain what it means to not have it). Besides, this is a lousy example of why it is not desirable to allow renames. The problem is simply that you have declarations appearing in a scope with no sane way to determine where they come from. But that also happens for instantiations, and I no longer see much difference between "integrated" renamed packages, and "integrated" instantiations. So the reason *I* was objecting to renames has become OBE (can't speak for others); the more important question for me is whether this whole idea sacrifices too much readability. (At least readability in the absence of a fancy IDE.) ... > Limited views (technical term) provide a very limited view (common > English) of things. True enough; I've been trying to explain my discomfort with this proposal with things that are different, but they don't necessarily seem important. That means my overall point is getting lost. ... > > You can also name the inner package in a use-clause, giving > > visibility to only part of the supposedly "integrated" whole. This > > seems bad from the view of a client, which really wants to see a real, flat view of the package. > > The point is that the client has the option of seeing a flat view of > the package. If the client wants to explicitly disambiguate a name by > referencing the inner package, that's up to the client. If you want to > disallow this, it could, I suppose, be detected by some > coding-standard-enforcement tool; that is outside of the scope of the > language definition. OK, here is the crux of the problem. As a package designer, I want to *limit* the ways that the client can refer to things. Otherwise, package maintenance becomes much harder. For instance, with this notion of "integrated" packages, it would be impossible to change back to a "flat" structure (because there can be references to the "integrated" package. Nor can you change to "integrated" packages from a "flat" structure without introducing incompatibilities. That can be bad. > > Part of the trouble here is the name of this construct: there is > > nothing really "integrated" about these packages. It's really a way > > to force a use-clause on a client. If they were really "integrated", > > we would see none of the unusual effects seen here. > > This is a relatively lightweight solution. The fact that it is based > on use-clauses definitely does show through in some cases. Yes, but you've missed my point completely. If you had called it something else (say "automatically used packages"), I wouldn't have quite the same objections. The *name* "integrated packages" gives an expectation of seamless merging; that isn't happening here, so the *name* is causing (unreasonable?) expectations. > On the other hand, if this solves the "exporting a private type and a > container thereof" problem (and I think it does), then that alone is a > strong argument for including this feature. Surely we need *some* solution to this problem, but I'm not certain this one is better than any of the other (now rejected) ideas. ... > As you noted, the early discussions of this AI were based on implicit > rename/subtype declarations and had nothing to do with use clauses. > > One problem with that approach was collisions. Implicit declarations > of non-overloadable constructs would have taken some work to > straighten out. I think such collisions should simply be illegal. Tough if that prevents declaring two container instances this way; we don't want homographs. > Use-visibility provided a reasonable approach to resolving these > issues that was already a well-defined part of the language, Yes, I know, I was a strong advocate for pursuing this solution. > If you think we should revive the rename/subtype approach or even > pursue an entirely new solution, either > a) provide a reasonably specific alternative that we can discuss or > b) get more of a consensus from the group that we want to > at least explore some other alternative and then I'll > try to write something up. Well, you know I don't have time to do either of these before the Brest meeting. But I'm definitely going to try to block progress on this one until I have time to do (a) or someone does (b). This proposal does not "feel right" to me. **************************************************************** From: Randy Brukardt Sent: Wednesday, May 27, 2009 10:06 PM > This proposal does not "feel right" to me. Let me add that the reasons are not that obvious and surely this is no reflection on the author of the proposal. (Part of the reason for complaining now is the hope that Tucker or I or someone will have an inspiration before the next meeting, that won't happen if the proposal is thought to be "good enough".) Anyway, my concerns are almost exclusively focused on the client's view of a package. I don't much care how complex the creation of a specification is, or how much change is necessary to do something in the specification, so long as the client view is controllable. But I would like the client view to be as close as possible to a "flat" structure with all of the interesting declarations appearing to exist in the outermost package. This proposal comes close but doesn't quite make it. --- One example that I've been thinking about is the proposed generic to provide iterators. If we do decide to define that "magic" generic, we will need to rearrange the Ada.Containers packages a lot to use it. But that rearrangement would seem to be incompatible in a number of ways, and limiting on implementation strategies to boot. To refresh, assume that we have a magic iterator generic (this is simplified for this example, as are the following packages) and we have Steve's "integrated packages": generic type Cursor is private; No_Element : in Cursor; package Ada.Iterator_Interfaces is type Basic_Iterator is limited interface; function First (Object : Basic_Iterator) return Cursor; function Next (Object : Basic_Iterator; Position : Cursor) return Cursor; end Ada.Iterator_Interfaces; [Note that the exact form of this generic doesn't matter much in terms of what rearrangements need to be done to the Ada.Containers packages.] Now, we currently have: generic type Element_Type is private; package Ada.Containers.Doubly_Linked_Lists is type List is tagged private; type Cursor is private; Empty_List : constant List; No_Element : constant Cursor; function Is_Empty (Container : List) return Boolean; procedure Clear (Container : in out List); function Element (Position : Cursor) return Element_Type; ... private ... -- not specified by the language end Ada.Containers.Doubly_Linked_Lists; In order to add Basic_Iterator visibly to type List, we'll have to rearrange this into using at least one nested package. The simplest I can come up with is: with Ada.Iterator_Interfaces; generic type Element_Type is private; package Ada.Containers.Doubly_Linked_Lists is use package Cursors is type Cursor is private; No_Element : constant Cursor; private ... -- not specified by the language end Cursors; package Iterators is new Ada.Iterator_Interfaces (Cursors, No_Element); type List is new Iterators with private; Empty_List : constant List; function Is_Empty (Container : List) return Boolean; procedure Clear (Container : in out List); function Element (Position : Cursor) return Element_Type; ... private ... -- not specified by the language end Ada.Containers.Doubly_Linked_Lists; Limited with incompatibilities don't matter in this case, as you can't get a limited view of a generic unit. Primitiveness incompatibilities are definitely in evidence, but I do have to wonder if it matters. In particular, if you derive from type Cursor currently, you will inherit function Element. But you will no longer inherit that routine with this rearrangement. I don't know how significant that is (I would suggest it is not very significant). Naming incompatibilities don't seem to be a problem with this example. They could be if we wanted to remove the "integrated" nested package (as someone could have named it explicitly in a use clause or in an expanded name). That's not likely to be an issue in this case. Since there is only two entities (plus the inherited "=") in the nested packages, we could just rename them and not need any special construct at all. But we'd still have the incompatibilities. (I did not expect this result when I started writing this message.) But there still is a significant issue. While we are not specifying what goes into the private parts, we do need to be able to writ them. A cursor needs a reference to a node and to the full container (the List in this example). The node probably could be defined in the private part of package Cursors, but we can't define a reference to List in the private part -- it hasn't been declared yet! And it *can't* be declared yet, because it depends on the interface that we haven't yet created. One way to workaround this problem is to use a Taft-amendment type in the private part of Cursors. But we can't complete that with the type List itself; we'd have to use a derived type. And that is obnoxious, forcing many junk type conversions. Even using class-wide types won’t get rid of those conversions, because they would be toward the root of the tree. Another possible workaround would be to clutter the spec with an incomplete type List before the nested package. But that could not be completed with the private extension List, so we would still end up with the same problem. In conclusion, this rearrangement works, has some client incompatibility (but it probably isn't important), but forces a major amount of junk type conversions in the body of the package. Of course, this is only one example, and it would be nice to look at additional examples in the future. **************************************************************** From: Tucker Taft Sent: Thursday, May 28, 2009 2:12 PM > Another possible workaround would be to clutter the spec with an > incomplete type List before the nested package. But that could not be > completed with the private extension List, so we would still end up with the > same problem. This particular problem seems fixable. The requirement that an incomplete type declaration be completed with a full_type_declaration seems arbitrary. There seems no inherent reason that it couldn't be completed with a private type or private extension declaration. The incomplete types produced from a "limited with" allow that, so the mechanism will be needed in Ada 2005 to handle such a completion anyway. > In conclusion, this rearrangement works, has some client > incompatibility (but it probably isn't important), but forces a major > amount of junk type conversions in the body of the package. Of course, > this is only one example, and it would be nice to look at additional examples > in the future. Normally I would have expected that almost the entire package would be included in an "integrated" package, and then this big nested package would be followed by various generic instantiations. But you have illustrated a case where that doesn't work, because you want one of the types to be built on the result of a generic instantiation. I wonder how common that is. How would you do it without this feature? As you point out, the integrated package is mostly just a short-hand for a bunch of renames, and in this case, there aren't that many. Hiding the nested package name automatically from use-visibility might be a helpful advantage of the proposed feature ;-). It does seem the ability to define an incomplete type that is completed by a partial view would be helpful, independent of this proposal. I wonder if your "magic iterator" generic has this problem in general, and perhaps it needs to be *defining* a cursor type, rather than taking it as a parameter. Or is it the fact that we want the container to be usable as an iterator? I think that is probably a bad idea in general, as it is inherently problematic to have the container carry some state about a particular iterator over the container. I understand the desire for a *syntax* approximating: for Element in Container loop ... end loop; But I see no requirement that this means we somehow have an iterator built into the Container itself. Also, we would certainly want to allow multiple tasks to iterate over a Container in parallel, I would think, or to allow nested iterators such as: for Left in Container loop for Right in Container loop Try_Pair(Left, Right); end loop; end loop; I think the above *syntax* should always presume there is an anonymous iterator object created, and that is what carries the state of the iteration. In any case, I wonder whether it is the strange nature of this generic that is creating the problem, or is there a common need to be able to define in a single package two recursively-dependent types one of which is based on a generic instantiation of the other. I suppose this is a very hard question to answer... Thanks for the example! **************************************************************** From: Randy Brukardt Sent: Thursday, May 28, 2009 2:49 PM ... > This particular problem seems fixable. The requirement that an > incomplete type declaration be completed with a full_type_declaration > seems arbitrary. There seems no inherent reason that it couldn't be > completed with a private type or private extension declaration. > The incomplete types produced from a "limited with" allow that, so the > mechanism will be needed in Ada 2005 to handle such a completion > anyway. Yes, I in fact wrote this up originally assuming that such completions were allowed. But before making a fool out of myself, I looked up the actual rules and changed this part accordingly. It would still clutter the spec a bit with an extra type declaration that doesn't really have anything to do with anything other than the implementation, but not a major problem overall. > > In conclusion, this rearrangement works, has some client > > incompatibility (but it probably isn't important), but forces a > > major amount of junk type conversions in the body of the package. Of > > course, this is only one example, and it would be nice to look at > additional examples in the future. > > Normally I would have expected that almost the entire package would be > included in an "integrated" package, and then this big nested package > would be followed by various generic instantiations. But you have > illustrated a case where that doesn't work, because you want one of > the types to be built on the result of a generic instantiation. I > wonder how common that is. No idea. It seems to need interfaces to make much sense, so it would mainly come up in new code. > How would you do it without this feature? As you point out, the > integrated package is mostly just a short-hand for a bunch of renames, > and in this case, there aren't that many. > Hiding the nested package name automatically from use-visibility might > be a helpful advantage of the proposed feature ;-). I was surprised to find out that it doesn't seem to matter much either way; the key is to use the nested package. Making it "integrated" is just icing. > It does seem the ability to define an incomplete type that is > completed by a partial view would be helpful, independent of this > proposal. Yes, I think so. > I wonder if your "magic iterator" generic has this problem in general, > and perhaps it needs to be *defining* a cursor type, rather than > taking it as a parameter. I don't know how that could work, given that container cursors include a reference to the container object. I suppose the entire thing could be created as a mix-in generic (adding to the container directly), but that seems annoying (isn't that exactly the sort of thing that interfaces were created to avoid?). > Or is it the fact that > we want the container to be usable as an iterator? I think that is > probably a bad idea in general, as it is inherently problematic to > have the container carry some state about a particular iterator over > the container. I strongly agree with this, but you need to convince Ed. :-) Maybe the rest of your message will help. > I understand the desire for a *syntax* approximating: > > for Element in Container loop > ... > end loop; I had proposed for Element in Container.Iterator loop ... end loop; (where "Iterator" is a function returning an anonymous iterator object). Since we're using that same sort of model for accessors (there is an "extra" discriminant reference in those), I don't see the problem. But of course, I was always happy with this formulation, it is other people that were not. > But I see no requirement that this means we somehow have an iterator > built into the Container itself. Also, we would certainly want to > allow multiple tasks to iterate over a Container in parallel, I would > think, or to allow nested iterators such as: > > for Left in Container loop > for Right in Container loop > Try_Pair(Left, Right); > end loop; > end loop; > > I think the above *syntax* should always presume there is an anonymous > iterator object created, and that is what carries the state of the > iteration. With the accessor proposal, you have to write a single extra selection; that seems fine to me for iterators, too. Since it would not be surprising to use both of them together, it would actually be kinda weird to have simpler syntax for the iterator and not the accessor: for Cursor in Container.Iterator loop Container.Accessor(Cursor).Element.Component := 10; end loop; > In any case, I wonder whether it is the strange nature of this generic > that is creating the problem, or is there a common need to be able to > define in a single package two recursively-dependent types one of > which is based on a generic instantiation of the other. I suppose > this is a very hard question to answer... I would expect a similar problem any time that you wanted to define an interface that needs customization with some properties. But it is hard to tell for sure, other than to say that designers are clever and come up with structures that no one expects. **************************************************************** From: Bob Duff Sent: Wednesday, June 17, 2009 4:16 PM > Append to the existing !problem section > > Consider a package which needs to export both a private type and an > instance of one of the container generics for that type. > > Currently, this requires something like > > package Pkg is > package Inner is > type T is private; > private > ... > end Inner; > > package I is new Some_Container_Generic (Inner.T); > end Pkg; > > What is wanted here is a way to make all of the declarations visible > in Pkg.Inner (and perhaps Pkg.I; that's less clear) The Pkg.I part seems clear to me. If I declare an array-of-T at that point, the array ops are visible to clients without any horsing around. If I declare a Vector or Linked_List of T, I want the same. The "array" is the canonical container abstraction. We should strive to make other containers as much like it as possible. >...visible as if > they were declared immediately within Pkg. > This special case of the problem alone has been viewed as being >sufficiently important to merit considering major changes in the >language (see the 4 alternative versions of AI05-0074). > > An unwanted nested package may also be introduced when a user wishes > to declare a non-primitive operation for a tagged type, as in > > package Pkg is > type T is tagged ... ; > > package Inner is > function Not_Primitive (X : T) return T; > end Inner; > end Pkg; > > As in the previous example, what is wanted here is a way to make all > of the declarations visible in Pkg.Inner visible as if they were > declared immediately within Pkg. > > ------- > > In !discussion section, append to the discussion item beginning with > > If it is decided that integrated package renames are not wanted, it > would be easy to remove them from the proposal. For what it's worth, I am like the "renames" part of the proposal. I don't buy the "it could be misused" argument. Yes, it could. But it can also be used well. Just like many other features of Ada. There may be issues with interactions with "limited with". We need to deal with that, but it doesn't kill the idea, for me. > Append: > > As Erhard has pointed out, there are good reasons to consider this > option. It is certainly possible to use integrated package renames > to write code that is very difficult to read; this was already true > of Ada83 use clauses, but integrated package renames seem to offer > substantially greater potential for confusion. > > Integrated package renames also make possible a number of odd corner cases: > > limited with Foo; > package P is > use package Bar renames Foo; > end P; > > package Outer is > package Inner is > use package Outer_Rename renames Outer; > end Inner; > end Outer; > > Finally, integrated package renames are not needed to solve any of > the problems that provided the original motivation for introducing > integrated packages. > > The wording changes need to delete integrated package renames from > this AI ^^^^ needed > would be straightforward. This would include: > - eliminating the wording changes in 8.5.3 > - replacing "integrated package or package renaming" with > "integrated package" throughout > - eliminating references to integrated package renamings > in the change appended after 8.4(11) > > -------- > > Append a new !discussion grocery-list item after the "grocery-list"? > "if integrated package renames are not wanted" item: > > Tuck suggests that the name of an integrated package rename > should not itself become "use"-visible when the enclosing package is "use"d. > > Consider the following example (supplied by Tuck): > Text_IO, Sequential_IO, and Direct_IO each include > renames of each exception declared in the package IO_Exceptions. > It is a no-brainer to replace those renames with a: > "use package renames Ada.IO_Exceptions;" > in Text_IO, Sequential_IO, and Direct_IO. > > However, what should the be? Well, most natural would be: > "use package IO_Exceptions renames Ada.IO_Exceptions;" > > but now suppose you have: > > with Ada.IO_Exceptions; > with Ada.Text_IO; > use Ada; > procedure What_Fun is > use Text_IO; > -- oops, IO_Exceptions suddenly disappears > begin > ... > exception > when IO_Exceptions.Name_Error => > -- oops, this doesn't compile any more! > end What_Fun; > > If it is decided that this is what we want, then corresponding > wording would probably be fairly straightforward (as long as > we don't also require elegance, in which case the problem > may be overconstrained). For what it's worth, I agree with Tucker on this point. > As an aside, note that the above example would be allowed if > "IO_Exceptions.Name_Error" were replaced with "Name_Error". > > Another solution would be to allow anonymous integrated package > renames, as in > use package <> renames Ada.Io_Exceptions; > This would be equivalent to > use package Some_Identifier_That_Occurs_Nowhere_Else_In_The_Program > renames Ada.Io_Exceptions; > Anonymous non-renames seem like a bad idea, but perhaps anonymous > integrated renames would be ok? Probably OK. > A third approach to this problem would be to add a name > resolution preference rule: if a package rename has the > same simple name as the renamed entity (which might or > might not itself be a rename), then prefer the renamed (and > similarly through chains of renames as long as the simple name > remains unchanged). > This would not introduce the sort of Beaujolais effects that > are typically associated with preference rules, but it does > seem that treating a simple-name-preserving rename differently > than a simple-name-nonpreserving rename might have some > peculiar consequences (e.g., there might be some odd interactions > with formal packages). This is probably a bad idea. Why is it a bad idea? In fact, why not extend it -- if name resolution produces a whole bunch of interpretations that all mean the same thing, why not say "pick one at random"? **************************************************************** From: Bob Duff Sent: Wednesday, June 17, 2009 4:17 PM > OK, here is the crux of the problem. As a package designer, I want to > *limit* the ways that the client can refer to things. Otherwise, > package maintenance becomes much harder. For instance, with this > notion of "integrated" packages, it would be impossible to change back to a "flat" > structure (because there can be references to the "integrated" > package. Nor can you change to "integrated" packages from a "flat" > structure without introducing incompatibilities. That can be bad. I agree with Randy, here. It doesn't kill the idea, for me, but it sure would be nice if we could make this stuff more invisible. The whole point is to make clients see a possibly-different structure than the implementation of the abstraction. >...This proposal does not "feel > right" to me. Well, to me it feels like "on the right track, but more work needed". **************************************************************** From: Bob Duff Sent: Wednesday, June 17, 2009 4:17 PM > It's often true that as we come to see the problems with one approach > in more detail, the path that we didn't take starts looking more and > more attractive. Yeah. And then we fail to solve real user-painful problems, because every proposed solution has some obscure language-lawyer problems. Sigh. **************************************************************** From: Bob Duff Sent: Wednesday, June 17, 2009 4:16 PM > You give as an example: > > package Pkg is > type T is tagged ... ; > > use package Inner is > function Not_Primitive (X : T) return T; > end Inner; > end Pkg; > > and mention that you would like to be able to make the name > Not_Primitive visible directly in Pkg. But this is exactly one of the > sorts of effects that bothers me about this proposal. Clients cannot > ignore the presence of the nested package, because its effects > re-emerge periodically. This is one of those cases: the primitiveness > changes, so for the purposes of deriving a type from T, you have to know the *real* structure of the package. How can that cause trouble? I mean, when you call a procedure dispatchingly, you get an error if it's not dispatching. **************************************************************** From: Randy Brukardt Sent: Thursday, June 18, 2009 11:27 AM > How can that cause trouble? I mean, when you call a procedure > dispatchingly, you get an error if it's not dispatching. Well, when writing (or debugging) your program, you will want to know whether or not something is inherited. That's pretty important for *static* calls (as well as dispatching calls), because calling Not_Primitive for an object of type Der_T isn't going to work. Sure, you'll get an error if it doesn't work, but compilation is hardly the time to find out that your design doesn't work. (And it may be hard to figure out *why* you are getting an error from the call.) It's even worse if you are use-adverse (like me) and name the routine with a full expanded name. What that name is could change dramatically. **************************************************************** From: Bob Duff Sent: Thursday, June 18, 2009 1:37 PM > Well, when writing (or debugging) your program, you will want to know > whether or not something is inherited. That's pretty important for > *static* calls (as well as dispatching calls), because calling > Not_Primitive for an object of type Der_T isn't going to work. Sure, > you'll get an error if it doesn't work, but compilation is hardly the > time to find out that your design doesn't work. Well, I can't get too excited about errors that are detected at compile time. >... (And it may be hard to figure out *why* you are getting an error >from the call.) True, it can. That's annoying. But it's still typically orders of magnitude cheaper to fix than a bug that escapes into customer's hands. > It's even worse if you are use-adverse (like me) and name the routine > with a full expanded name. What that name is could change dramatically. Well, I suppose if you're use-averse, you won't use "use package" much. It's unfortunate that the Ada world is split into "use haters" and "use lovers", but I don't see any solution to that problem. (I'm somewhere in between, by the way, and I change my mind toward either end of the spectrum, back and forth over the years.) **************************************************************** From: Randy Brukardt Sent: Thursday, June 18, 2009 2:14 PM We won't have any choice. "use package" (or whatever we come up with) will be used in commonly used libraries (possibly including language-defined ones). Ignoring it is somewhat of an option, but having the possibility of multiple views harms maintenance and has an effect on compatibility. I'd rather see a solution that "collapsed" the package completely so that the clients have no visibility at all on the fact that nested packages are used. To do that safely, we'd have to have a mechanism for preventing conflicts (one way would be to make conflicts illegal, but that would be limiting), so as to avoid the hiding problem that Erhard pointed out. Maybe we need to go back to the <> package and look at other ways to eliminate the hiding problem. Tucker wants to hide the name anyway, so it seems that the package name is the least important part of the mechanism. **************************************************************** From: Steve Baird Sent: Friday, June 19, 2009 10:40 AM >> Append a new !discussion grocery-list item after the > > "grocery-list"? > I think Randy once observed that some of my !discussion sections read like a grocery list. >> A third approach to this problem would be to add a name >> resolution preference rule: if a package rename has the >> same simple name as the renamed entity (which might or >> might not itself be a rename), then prefer the renamed (and >> similarly through chains of renames as long as the simple name >> remains unchanged). >> This would not introduce the sort of Beaujolais effects that >> are typically associated with preference rules, but it does >> seem that treating a simple-name-preserving rename differently >> than a simple-name-nonpreserving rename might have some >> peculiar consequences (e.g., there might be some odd interactions >> with formal packages). This is probably a bad idea. > > Why is it a bad idea? > In fact, why not extend it -- if name resolution produces a whole > bunch of interpretations that all mean the same thing, why not say "pick one at random"? > Maybe you are right. This sounds like a fairly major change that would have to be looked at very carefully. We'd want to preserve the "implicit rename" model for the binding of generic actuals to formals.\ Do you think this is worth looking into more closely? Bob Duff wrote: > Randy Brukardt wrote: > >> OK, here is the crux of the problem. As a package designer, I want to >> *limit* the ways that the client can refer to things. Otherwise, >> package maintenance becomes much harder. For instance, with this >> notion of "integrated" packages, it would be impossible to change back to a "flat" >> structure (because there can be references to the "integrated" >> package. Nor can you change to "integrated" packages from a "flat" >> structure without introducing incompatibilities. That can be bad. > > I agree with Randy, here. It doesn't kill the idea, for me, but it > sure would be nice if we could make this stuff more invisible. One way to achieve this would be to allow anonymous packages even in the non-rename case, as in package Outer is use package <> is ...; end <>; end Outer; I don't think this interacts well with the use-visibility-based approach that we have been discussing. There are times when use-visibility doesn't get you what you want and you need the fallback mechanism of explicit qualification. This construct would remove that option. On the other hand, it sounds like removing the option of explicit qualification is exactly what you and Randy want. It preserves the desired "flat" interface. I had lunch with Pascal Leroy yesterday and we discussed this AI briefly. He didn't claim to have looked at this one in depth, but he did observe that the implicit-rename-declaration approach that was being considered initially seemed more intuitive than the current approach that is based on use-visibility. Do you think the rename-based approach would somehow help with this problem - making these inner packages invisible while still allowing the user to name everything that needs naming. As far as I can see the answer is "no", but I thought I would raise the question. **************************************************************** From: Tucker Taft Sent: Friday, June 19, 2009 11:40 AM The implicit rename and the use-visibility differ primarily only when there is a conflict. Implicit rename suffers from Beaujolais if you allow conflicts, while use-visibility doesn't. I don't think we want to introduce Beaujolais effects, so I presume we are talking a model where conflicts are illegal. Perhaps a compromise might say that if you give a name, then conflicts are permitted (and use-visibility rules apply), while if you don't give a name, then conflicts are illegal, so it is more like the implicit rename model. I suppose the other area of difference is primitiveness of operations. I thought it was a feature that you could get non-primitive operations to be directly visible, essentially like those declared in the body. Perhaps the same rule would apply: if you give a name, then the subprograms are *not* primitive of a type in the enclosing scope, whereas if you use "<>" then the subprograms *do* become primitives of such types. **************************************************************** From: Tucker Taft Sent: Wednesday, June 24, 2009 12:59 PM Any comments on this thought of having "use package <> ..." follow renaming semantics (with no conflicts permitted), and "use package Integrated ..." follow transitive "use-visibility" semantics (with conflicts resolved in the use-ish way, and a name available to disambiguate)? **************************************************************** From: Randy Brukardt Sent: Wednesday, June 24, 2009 1:10 PM I have to wonder if we *really* need both. But if we do, the semantics you suggest seem correct. I'm pretty convinced that we need the nameless version because of all of the reasons that have been previously noted about compatibility, the client view, name space pollution, etc. I'm less convinced that we need the named version if we have the nameless version (because of those same reasons mentioned above), but I suppose there might be significant use cases in its favor. It surely would be annoying if some maintenance far away was to make a program illegal (by adding a conflict) and there being no good way to avoid the problem. BTW, if we do this, do we "fix" Text_IO to have use package <> renames Ada.IO_Exceptions; instead of the batch of individual renames of exceptions?? **************************************************************** From: Tucker Taft Sent: Wednesday, June 24, 2009 1:40 PM > I have to wonder if we *really* need both. But if we do, the semantics > you suggest seem correct. Good. That's a start. ... > BTW, if we do this, do we "fix" Text_IO to have > use package <> renames Ada.IO_Exceptions; instead of the batch of > individual renames of exceptions?? Yes, that would make sense. **************************************************************** From: Steve Baird Sent: Wednesday, June 24, 2009 2:45 PM I like the idea of guaranteeing that the name Enclosing_Package.Some_Entity will resolve in precisely the case where we are taking away the fallback option of using the name Enclosing_Package.Named_Integrated_Package.Some_Entity . There are, however, the usual problems with having two very similar complex constructs which are different in subtle but important ways. This approach (deliberately) spoils the equivalence between use package <> ... ; and use package An_Identifier_That_Occurs_Nowhere_Else ... ; , which seems potentially confusing. Could we revisit the approach of allowing named and anonymous integrated packages while unconditionally using "rename" semantics, perhaps allowing collisions between renames (including constraintless subtypes) which denote the same thing? In other words, throw out the whole use-clause-based approach? When we first explored that approach, the problems with collisions looked pretty bad. If, however, the alternative is the one you suggest (if it's named, use one complicated model; if anonymous, use another), then maybe it is time to have another look to see if those collision problems were really that bad. -------- Would anonymous packages be limited to renames and instances? Perhaps only renames? I don't want to see "package body <> is". -------- There may be problems with the rename approach and disallowing conflicts in the case of an integrated instantiation. I'm thinking about child units of the instantiated generic. generic package G1 is end G1; with G1; package Pkg is package <> is new G1; G2 : Integer; end Pkg; generic package G1.G2 is end G1.G2; with G1.G2; with Pkg; package Troublemaker is -- what is the story on Pkg.G2 here ? end Troublemaker; Perhaps you only get implicitly declared renames of the items which are visible in the integrated package spec at the point of the integrated package's declaration. -------- For anonymous packages, the <> identifier tells us everything we need to know and the word "use" is redundant. Would it make matters clearer or more confusing if we took advantage of this and dropped the word "use" so that the syntax would be, for example, package <> renames Ada.IO_Exceptions; instead of use package <> renames Ada.IO_Exceptions; ? If we decide that anonymous packages get "rename" rather than "use" semantics, this might add weight to the argument for dropping the word "use" in that case. If we decide that we are not going to support named integrated packages at all, then it seems clear to me that there is no need for the word "use" in the syntax. **************************************************************** From: Tucker Taft Sent: Wednesday, June 24, 2009 3:07 PM I agree that we could drop the "use" for the renaming-based semantics: package <> renames ...; and perhaps package <> is new Gen_Pkg(...); I had forgotten the problem of "package body <> is" in generalizing this to handle integrated, non-rename, non-instance packages. I would hate to give up on the "normal" nested package case, as that was our technique for solving the instantiating-with-private-type problem. I suppose we could allow at most one such nested package, so the "package body <> is" would be unambiguous. Round and round we go... ;-) **************************************************************** From: Steve Baird Sent: Wednesday, June 24, 2009 4:07 PM > I would hate > to give up on the "normal" nested package case, as that was our > technique for solving the instantiating-with-private-type problem. I agree. I would have to hear a very persuasive argument before I would be interested in any design alternative for integrated packages that cannot be used to solve that problem. > I suppose we could allow at most one such nested package, so the > "package body <> is" would be unambiguous. Yuck. Requiring that the anonymous package cannot require a body is also no good. **************************************************************** From: Randy Brukardt Sent: Thursday, June 25, 2009 9:38 PM > > I suppose we could allow at most one such nested package, so the > > "package body <> is" would be unambiguous. > > Yuck. Requiring that the anonymous package cannot require a body is > also no good. I'm not sure I understand "yuck" in this context. It seems that it makes sense to allow only one anonymous package (of any kind) per scope, and thus there couldn't be any confusion about which one has the body. That also would greatly reduce the possibilities for conflicts and surely simplify the implementation of checking for conflicts. It's not like you could have a whole bunch of anonymous instances anyway: with the "no conflicts" model, you are quickly going to hit some brick wall. For instance, it would not be possible to have more than one anonymous instance of a predefined container, because they all contain a constant No_Element (which is not overloadable). And it surely is obvious that you can only have one anonymous regular package. So it might make sense to simply allow only one per scope to avoid confusion. I doubt that this would be a major hardship, at least for the relatively simple uses that we've envisioned. We're trying to be able to add a container to the visible part of a package, not necessarily a whole flock of them. Still, I suppose this makes it clear why we would want to continue to support the named version as well -- the anonymous version would be limited in applicability (but preferable if it could be used). P.S. It's wonderful that no idea for fixing the instantiating-with-private-type problem works for long, unless it is hard enough to use that everyone [other than me and Brad :-)] rejects it. :-( **************************************************************** From: Steve Baird Sent: Thursday, June 25, 2009 9:59 PM > I'm not sure I understand "yuck" in this context. A rule that allows one "special" construct just seems a bit ugly to me. If we are going to go down this route, I'd rather consider something equivalent to (but hopefully better looking than) package is end ; package is end ; so that the two (or more) anonymous packages cannot be named by an identifier, but their completions can be disambiguated. If we do decide to allow package bodies for anonymous packages, we still should not allow such bodies to be separate; that would have too much impact on tools, file naming conventions, etc. **************************************************************** From: Randy Brukardt Sent: Thursday, June 25, 2009 10:21 PM You mean as in subunits and separate compilation? I think that follows from the fact that we don't allow much for subunits: no overloading, no operators, no name conflicts. I don't think we'd need to say anything at all, since we'd need to *add* syntax to allow anonymous stubs -- and I can't imagine any reason for doing that. **************************************************************** From: Ed Schonberg Sent: Friday, June 26, 2009 8:45 AM > It's not like you could have a whole bunch of anonymous instances > anyway: > with the "no conflicts" model, you are quickly going to hit some brick > wall. > For instance, it would not be possible to have more than one anonymous > instance of a predefined container, because they all contain a > constant No_Element (which is not overloadable). But a rule that says "you can only have one package" will immediately strike users as arbitrary. Why should I be able to define a single container in a spec? > And it surely is obvious that you can only have one anonymous regular > package. Then that dooms anonymous packages. > > So it might make sense to simply allow only one per scope to avoid > confusion. > > I doubt that this would be a major hardship, at least for the > relatively simple uses that we've envisioned. We're trying to be able > to add a container to the visible part of a package, not necessarily a > whole flock of them. > > Still, I suppose this makes it clear why we would want to continue to > support the named version as well -- the anonymous version would be > limited in applicability (but preferable if it could be used). > > Randy. > > P.S. It's wonderful that no idea for fixing the > instantiating-with-private-type problem works for long, unless it is > hard enough to use that everyone [other than me and Brad :-)] rejects > it. :-( If the central purpose of this AI is to ease the use of containers, then I think AI05-74 (3) namely deferred freezing rules for instantiations deserves another look. It is a minimal change, Discussion on it was sidetracked by other concerns, but I have not seen crippling arguments against it. Steve has proposed a pragma to prevent contract issues, what are the big objections to this? **************************************************************** From: Tucker Taft Sent: Friday, June 26, 2009 9:02 AM My objection to Steve's proposal is precisely because it relies on a pragma. I believe whatever we do deserves some kind of distinguishing syntax. Note that my suggestion was that we would only allow at most one integrated, anonymous non-instance, non-rename package. There seems no difficulty allowing any number of anonymous instances/renamings, as these don't need bodies. Also, there seems no difficulty in allowing any number of named non-instance, non-rename integrated packages. Allowing only one "normal" nested package to be anonymous seems pretty reasonable. The only reason for making a nested package anonymous and integrated seems to be to provide for freezing uses of private types within the enclosing package. It doesn't seem like you would need more than one such anonymous nested package in typical scenarios. **************************************************************** From: Steve Baird Sent: Friday, June 26, 2009 9:32 AM > But a rule that says "you can only have one package" will immediately > strike users as arbitrary. I agree. > Allowing only one "normal" nested package to be anonymous seems pretty > reasonable. The only reason for making a nested package anonymous and > integrated seems to be to provide for freezing uses of private types > within the enclosing package. It doesn't seem like you would need > more than one such anonymous nested package in typical scenarios. What about a package that wants to export two private types, as in package Pkg is package is type T1 is private; -- primitive operations of T1; end ; package T1_Containers is new Some_Container_Generic (T1); package is type T2 is private; -- primitive operations of T2, perhaps -- involving parameters of type T1_Container.Some_Type end ; package T2_Containers is new Some_Container_Generic (T2); end Pkg; ? **************************************************************** From: Tucker Taft Sent: Friday, June 26, 2009 12:22 PM I believe the following would work: package Pkg is package <> is package <> is type T1 is private; -- primitive operations of T1; end <>; package T1_Containers is new Some_Container_Generic (T1); type T2 is private; -- primitive operations of T2, perhaps -- involving parameters of type T1_Container.Some_Type end <>; package T2_Containers is new Some_Container_Generic (T2); end Pkg; I believe it is possible to show that by nesting package <>, you can achieve whatever you want. Of course, this is now the moral equivalent of allowing multiple visible parts, separated by private/end-private. **************************************************************** From: Randy Brukardt Sent: Friday, June 26, 2009 12:25 PM > If the central purpose of this AI is to ease the use of containers, > then I think AI05-74 (3) namely deferred freezing rules for > instantiations deserves another look. It is a minimal change, > Discussion on it was sidetracked by other concerns, but I have not > seen crippling arguments against it. Steve has proposed a pragma to > prevent contract issues, what are the big objections to this? I agree with Tucker that we don't want something so fundamental to be a pragma. Syntax would be preferred. Steve also had a number of "Baird cases" which complicated the model considerably (just as in anonymous packages, use package, and all of the other ideas that we've looked at). Finally, there is nothing "minimal" about this change. It would be an implementation earthquake in Janus/Ada (since we share both the spec and body of generics, and depend heavily on freezing to prevent problems). And it would add some conceptual complexity to generics (now there would be two different sets of freezing rules for them) I continue to prefer a version of the "forward instantiation" (probably exporting limited views) as the best solution, but I admit that we've never found a syntax which makes sense both from the client view (where the forward instance looks like a normal instance) and from the local package view (where the forward instance is special). Note that the proposed extension of uses of incomplete types would also have the effect of making this idea much more powerful. Brad has been exploring variations on this theme, perhaps he's found the perfect solution while we weren't paying attention. **************************************************************** From: Robert Dewar Sent: Friday, June 26, 2009 12:34 PM > Finally, there is nothing "minimal" about this change. It would be an > implementation earthquake in Janus/Ada (since we share both the spec > and body of generics, and depend heavily on freezing to prevent > problems). And it would add some conceptual complexity to generics > (now there would be two different sets of freezing rules for them) I think we should get an estimate of implementation impact from multiple implementations before we decide on this feature. I am dubious, seems like the motivation here comes from the designers rather than users, but I may be wrong on this, do we have cases of large scale users complaining about this missing functionality? > I continue to prefer a version of the "forward instantiation" > (probably exporting limited views) as the best solution, but I admit > that we've never found a syntax which makes sense both from the client > view (where the forward instance looks like a normal instance) and > from the local package view (where the forward instance is special). > Note that the proposed extension of uses of incomplete types would > also have the effect of making this idea much more powerful. > > Brad has been exploring variations on this theme, perhaps he's found > the perfect solution while we weren't paying attention. I sure share the feeling that we could find some better solution here, but i realize the search has not been fruitful so far. **************************************************************** From: Ed Schonberg Sent: Friday, June 26, 2009 12:51 PM > I agree with Tucker that we don't want something so fundamental to be > a pragma. Syntax would be preferred. I don't understand this comment: if a pragma is sufficient, then it is NOT a fundamental change, and there is very little to add to the RM. Looks like a plus to me. > Steve also had a number of "Baird cases" which complicated the model > considerably (just as in anonymous packages, use package, and all of > the other ideas that we've looked at). I trust that Steve will provide other admirably byzantine examples on any solution, but the ones I have seen are no worse than for integrated packages. > Finally, there is nothing "minimal" about this change. It would be an > implementation earthquake in Janus/Ada (since we share both the spec > and body of generics, and depend heavily on freezing to prevent > problems). And it would add some conceptual complexity to generics > (now there would be two different sets of freezing rules for them) Sorry, implementation difficulties are minor objections if we can get the language right. The change in freezing rules will add a few lines to 13.14. In comparison, integrated packages (named and anonymous) will require plenty of text in the RM and AARM to explain why they are there, when they should be used, why they are preferable (perhaps) to child units, etc. > I continue to prefer a version of the "forward instantiation" > (probably exporting limited views) as the best solution, but I admit > that we've never found a syntax which makes sense both from the client > view (where the forward instance looks like a normal instance) and > from the local package view (where the forward instance is special). > Note that the proposed extension of uses of incomplete types would > also have the effect of making this idea much more powerful. > > Brad has been exploring variations on this theme, perhaps he's found > the perfect solution while we weren't paying attention. OK, worth looking at in more detail. **************************************************************** From: Robert Dewar Sent: Friday, June 26, 2009 1:05 PM > Sorry, implementation difficulties are minor objections if we can get > the language right. The change in freezing rules will add a few lines > to 13.14. In comparison, integrated packages (named and anonymous) > will require plenty of text in the RM and AARM to explain why they are > there, when they should be used, why they are preferable (perhaps) to > child units, etc. We have *never* taken the position that implementation difficulties are to be ignored, and we shouldn't start now. I would like to see at the minimum an analysis from AdaCore, Tuck, and Rational on forseen implementation difficulties, given this is a non-critical feature. I say it is non-critical, since I don't see its absence as having a significant effect on Ada adoption. To me, at this stage, EVERY proposal for language change should include an analysis of impact on implementations. **************************************************************** From: Randy Brukardt Sent: Friday, June 26, 2009 3:48 PM > > I agree with Tucker that we don't want something so fundamental to > > be a pragma. Syntax would be preferred. > > I don't understand this comment: if a pragma is sufficient, then it is > NOT a fundamental change, and there is very little to add to the RM. > Looks like a plus to me. I don't understand this comment: there is a massive amount of semantic change (at a minimum concerning freezing and elaboration) behind this difference. All of this change clearly violates good taste in pragmas. You could argue that there are a number of existing pragmas that also violate this good taste, but I don't think that two wrongs make a right. Just because we screwed up in the past does not mean that we should do so again. Note that we considered and rejected pragma-based solutions in a number of other areas; this doesn't seem that different to me. > > Steve also had a number of "Baird cases" which complicated the model > > considerably (just as in anonymous packages, use package, and all of > > the other ideas that we've looked at). > > I trust that Steve will provide other admirably byzantine examples on > any solution, but the ones I have seen are no worse than for > integrated packages. That's possible; I was just skimming the e-mail in the AI, and it showed a number of new interactions that would have to be addressed (presumably with additional rules). > > Finally, there is nothing "minimal" about this change. It would be > > an implementation earthquake in Janus/Ada (since we share both the > > spec and body of generics, and depend heavily on freezing to prevent > > problems). And it would add some conceptual complexity to generics > > (now there would be two different sets of freezing rules for them) > > Sorry, implementation difficulties are minor objections if we can get > the language right. The change in freezing rules will add a few lines > to 13.14. In comparison, integrated packages (named and anonymous) > will require plenty of text in the RM and AARM to explain why they are > there, when they should be used, why they are preferable (perhaps) to > child units, etc. Robert made the point about implementation better than I could have. I think you are dreaming if you think that only "a few lines [in] 13.14" are needed here. No significant proposal, no matter how apparently simple, ends up that easy. In this case, we surely will have to make changes in the elaboration rules for instances, define the syntax (or pragma) that enables these semantic changes, and add additional rules to fix the interactions that Steve pointed out. We may also want to use the new feature in some of the existing predefined packages. I would be very, very surprised if the number of changes were less than those needed in Steve's latest draft of AI05-135 (which seems to cover everything required for "integrated packages" with the exception of use in existing predefined packages). In any case, explaining "why they are there" and especially "why they are preferable" belongs in textbooks (and the AI), not the standard and is pretty much irrelevant to any discussion of the size of a change to the language. **************************************************************** From: Brad Moore Sent: Saturday, June 27, 2009 5:35 PM > If the central purpose of this AI is to ease the use of containers, > then I think AI05-74 (3) namely deferred freezing rules for > instantiations deserves another look. It is a minimal change, > Discussion on it was sidetracked by other concerns, but I have not > seen crippling arguments against it. Steve has proposed a pragma to > prevent contract issues, what are the big objections to this? My concern regarding the pragma approach is that I think we should avoid putting things (pragmas) in the "server" (the generic package), that makes assumptions about how the "client" will want to use the package. If we went with this approach, then I envision the next incarnation of the Ada style guide (hopefully we will see someday) would be modified to say, "Use this new pragma everywhere on every new generic if at all possible, and do not declare variables or expressions in the generic specs, unless you can provide strong justifications, otherwise you may be limiting the reuse possibilities for your generic code." This also seems like a maintenance problem. If the maintenance of a generic package needs to add a variable declaration or expression to the package, (and thereby remove the special pragma) then this could break clients, which may not notice the change until the next time a build for a particular program is executed. By then other clients may be dependent on the newer version of the generic code. Basically, I don't think we want to go there. My thoughts were that it is better to not change the way generics work. (i.e., no restrictions on declaring variables or expressions), and instead put any restrictions needed to prevent problems in the client. This is why I do not have any such pragma in my most recent proposal. (It isn't in my most recent version of AI05-0074-4 either, though earlier incarnations did have it, as I originally "borrowed" the idea from AI05-0074-3) I haven't thought too much about whether AI05-0074-3 could be reworked, in a manner similar to AI05-0074-5? to eliminate the pragma, though I think *something* (whether it be syntax or pragma) is needed to distinguish between this usage and a normal instantiation. **************************************************************** From: Robert Dewar Sent: Saturday, June 27, 2009 5:42 PM > This also seems like a maintenance problem. If the maintenance of a > generic package needs to add a variable declaration or expression to > the package, (and thereby remove the special pragma) then this could > break clients, which may not notice the change until the next time a > build for a particular program is executed. By then other clients may > be dependent on the newer version of the generic code. I don't really see this point. ANY change to ANY package may break clients, and surely you don't go changing a generic spec without a lot of discussion and testing if it is in general use. **************************************************************** From: Randy Brukardt Sent: Saturday, June 27, 2009 6:08 PM As a pragma, I agree with Brad, simply because this pragma would violate "good taste in pragmas". We recently added some discussion to the AARM to explain what good taste is (see 2.8(1.a/3) or AI05-0100-1 if you don't have the draft 7 AARM handy). In particular, removing a pragma from a legal program should not make the program illegal, and this proposed pragma would have that effect. (Adding a pragma can make a legal program illegal, however.) If we are talking about syntax instead, however, I agree with Robert. It would be amazing to make major changes to the syntax of a package specification (generic or not) and expect clients to be unaffected. So I don't find this point to be very compelling. The style guide issue doesn't bother me much, either; it seems to be a good practice to avoid this stuff in the specification anyway (it causes too much coupling), so a style requiring that doesn't seem that terrible. There is plenty of stuff that Ada allows you to do that you should really avoid. I definitely agree that there needs to be syntax somewhere to distinguish this special case from the more normal rules, for compatibility if for no other reason. (Whatever special case we decide on.) **************************************************************** From: Robert Dewar Sent: Saturday, June 27, 2009 6:19 PM > As a pragma, I agree with Brad, simply because this pragma would > violate "good taste in pragmas". We recently added some discussion to > the AARM to explain what good taste is (see 2.8(1.a/3) or AI05-0100-1 > if you don't have the draft 7 AARM handy). In particular, removing a > pragma from a legal program should not make the program illegal, and > this proposed pragma would have that effect. (Adding a pragma can make > a legal program illegal, > however.) Well i can't get too excited about that, since we already have pragmas that are in bad taste from this point of view. I don't really mind if these are language defined pragmas, actually come to think of it I don't mind at all. Language definitions should not be in the business of defining what is "good taste" in any case, so anything like that in the standard is going to be ignored by me anyway. We design pragmas to be useful, not to meet some committee's idea of what is good taste :-) :-) If you think a pragma is in bad taste, you don't have to use it! > If we are talking about syntax instead, however, I agree with Robert. > It would be amazing to make major changes to the syntax of a package > specification (generic or not) and expect clients to be unaffected. So > I don't find this point to be very compelling. OK, well new syntax is fine by me too, though of course it is always more troublesome in practice. **************************************************************** From: Randy Brukardt Sent: Saturday, June 27, 2009 6:46 PM > Well i can't get too excited about that, since we already have pragmas > that are in bad taste from this point of view. I don't really mind if > these are language defined pragmas, actually come to think of it I > don't mind at all. Language definitions should not be in the business > of defining what is "good taste" in any case, so anything like that in > the standard is going to be ignored by me anyway. We design pragmas to > be useful, not to meet some committee's idea of what is good taste > :-) :-) If you think a pragma is in bad taste, you don't have to use > it! That's not really true for language-defined pragmas. Pragma Import is a pragma in bad taste, but it's pretty much impossible to avoid using it (as much as I'd like to). Imagine trying to write Claw without using pragma Import! :-) > > If we are talking about syntax instead, however, I agree with Robert. > > It would be amazing to make major changes to the syntax of a package > > specification (generic or not) and expect clients to be unaffected. > > So I don't find this point to be very compelling. > > OK, well new syntax is fine by me too, though of course it is always > more troublesome in practice. It can't be that much more troublesome, if you've already managed to implement conditional expressions. :-) :-) Seriously, syntax has a bit more impact on tools than a pragma would, but otherwise it isn't much different. I prefer either to just changing the semantics of something, which leads to all kinds of complications both in implementation and in use. **************************************************************** From: Robert Dewar Sent: Saturday, June 27, 2009 6:50 PM > As a pragma, I agree with Brad, simply because this pragma would > violate "good taste in pragmas". We recently added some discussion to > the AARM to explain what good taste is (see 2.8(1.a/3) or AI05-0100-1 > if you don't have the draft 7 AARM handy). In particular, removing a > pragma from a legal program should not make the program illegal This really isn't vaguely true for existing pragmas! For example, removing pragma Pack often makes a program illegal. Removing a pragma Import almost certainly makes a program illegal. Removing a pragma Convention can easily make a program illegal Removing a pragma Pure, Preelaborate, or any of the other categorization pragmas can make a program illegal. Removing a pragma Elaborate can make a program illegal same of course with Elaborate_All and Elaborate_Body. Removing a pragma Atomic or Volatile can make a program illegal. There aren't that many pragmas defined in the RM, and as we see many of them are in "bad taste" :-) Once we have done such a bad job of following our own bad taste rules, I can hardly get upset at adding one more. And since the language is so free in adding these bad taste pragmas (note that most of these were added in Ada 95), I see no reason for implementors to feel bound to stricter standards. I guess to me, it is MUCH worse when removing a pragma can cause the progam to become erroneous, or malfunction in mysterious ways. Ilegality is a nice outcome of improperly removing a pragma compared to these alternatives! Notionally if an implementation has only "good taste" pragmas, then you can remove them all or ignore them all, and the program is still legal. But so what? Pragmas are not there for amusement purposes, almost certainly they are important to the correct functioning of the program. If you have a GNAT program that uses pragma Weak_External, sure you can remove it and the compiler will consider the program legal, but good luck at link time :-) And if you remove a pragma Wide_Character_Encoding, it will be quite surprising if you can still compile your source :-) [Most of the rest of the pragmas thread can be found in AI05-0163-1. - Editor.] **************************************************************** From: Robert Dewar Sent: Saturday, June 27, 2009 6:59 PM > It can't be that much more troublesome, if you've already managed to > implement conditional expressions. :-) :-) I am not concerned with implementation difficultioes, obviously it does not matter two hoots to the implementation whether something is syntax or pragmas, to an implementation, a pragma *is* syntax. I was concerned about incompatibilities, certainly for instance introducing new keywords has serious impacts. And new syntax is far more likely to cause trouble with tools like pretty printers, syntax oriented editors, ASIS etc. > Seriously, syntax has a bit more impact on tools than a pragma would, > but otherwise it isn't much different. I prefer either to just > changing the semantics of something, which leads to all kinds of > complications both in implementation and in use. Definitely I agree on that, the change to returning limited types caused chaos for our users, and delayed many of them in moving to Ada 2005. **************************************************************** From: Brad Moore Sent: Sunday, June 28, 2009 12:51 AM > If we are talking about syntax instead, however, I agree with Robert. > It would be amazing to make major changes to the syntax of a package > specification (generic or not) and expect clients to be unaffected. So > I don't find this point to be very compelling. > > The style guide issue doesn't bother me much, either; it seems to be a > good practice to avoid this stuff in the specification anyway (it > causes too much coupling), so a style requiring that doesn't seem that > terrible. There is plenty of stuff that Ada allows you to do that you should really avoid. I agree that both these points are not serious, show-stopper type problems. We could certainly live with them if we had to. I guess my point is that regardless how serious these are, they can still be viewed as being negatives. If we can achieve more or less the same thing without the negatives, then that may be even more desirable. **************************************************************** From: Brad Moore Sent: Sunday, June 28, 2009 12:33 PM > I agree with Randy that we should avoid using pragmas that make things > legal that would otherwise be illegal (ignoring representational issues). > Yes we have some such pragmas now, but there are few, and it was back > in Ada 9X that we established this principle and did our best to stick > with it. > I don't like to see us slipping backwards now. I also think there is something to be said for having the distinguishing syntax (or pragma if we must) at the site of where we want the benefits of that syntax. i.e., close to the instantiation in the unit that wants to instantiate a generic using a private type, rather than in the generic unit being instantiated. For example, I still see a maintenance problem. If a client discovers a need to instantiate some existing generic using a private type, modifying the generic to add the pragma may not be an option, if the generic is in a unit supplied by a 3rd party vendor, or if removing any existing declarations or expressions in the generic would break too many other clients. **************************************************************** From: Robert Dewar Sent: Sunday, June 28, 2009 12:43 PM makes sense to me! though the devil is in the details for something like this. **************************************************************** From: Brad Moore Sent: Friday, July 10, 2009 10:47 AM I have another approach for addressing the private type instantiation issue. On the surface, it appears to have a lot of the desirable qualities of the integrated packages solution, but without some of the not so desirable qualities. I think it is the implicitness of the integrated package solution that is generating some uneasiness. It is the implicitness that led us to look (so far unsuccessfully) at the anonymous package idea as a means of avoiding conflicts. I also think it is desirable if we can make it more explicit about which services we want to export from the instantiation. What if instead we allowed type renames to solve this issue? The idea is that the type rename would export all operations involving that type from the inner package (or instance). For enumeration types, this would export all the literals of the enumeration. This gives the client the ability to provide a name for the type, thus avoiding conflicts, and eliminates the need to look at the equivalence of the anonymous packages idea. If you want to export a subprogram that involves more than one type declared in the inner package, (or instance), then you would need to also declare a type rename for those other types. (or otherwise be required the name to be qualified to reference the inner type). eg. with Ada.Containers.Doubly_Linked_Lists; with Ada.Containers.Vectors; package P is package Nested is type T is private; private type T is ...; end Nested; type R renames Nested.T; -- R makes T available outside Nested package Lists is new Ada.Containers.Doubly_Linked_Lists (R); -- Make the Lists services directly available in package P; type List renames Lists.List; type Iterator renames Lists.Cursor; No_List_Element renames Lists.No_Element; -- Another instantiation without any conflicts package Arrays is new Ada.Containers.Vectors (Index_Type => Natural, Element_Type => R); type Array renames Arrays.Vector; type Array_Cursor renames Arrays.Cursor; No_Array_Element renames Arrays.No_Element; end P; with P; procedure Q is My_List : P.List; -- Really is P.Lists.List My_Cursor : P.Iterator; -- Really P.Lists.Cursor My_R : P.R; -- Really P.Nested.T begin My_List.Append (My_R); -- Really P.Lists.Append My_Cursor := My_List.First; end Q; The integrated package idea also would export object declarations and exception declarations. This solution probably wouldn't export object declarations, and would not export exception declarations since those are not tied to type declarations, but having to explicitly rename those might be considered a good thing. Is this idea worth developing further? **************************************************************** From: Steve Baird Sent: Friday, July 10, 2009 12:05 PM > What if instead we allowed type renames to solve this issue? > > The idea is that the type rename would export all operations involving > that type from the inner package (or instance). > This would certainly be a heck of a lot simpler than integrated packages. It is less general, but if it is powerful enough to solve the problems that integrated packages were intended to solve, then that's all we need. Having two similar-but-slightly-different constructs in the language (i.e. "subtype S is Pkg.T;" vs. "type S renames Pkg.T;") may be confusing to some users at first, but is this any worse in this respect than subtypes vs. derived types or qualified expressions vs. type conversion? > Is this idea worth developing further? I think so. The first question that comes to my mind is whether there are problems that need solving for which (some version of) integrated packages provide a solution and this proposal does not. **************************************************************** From: Ed Schonberg Sent: Friday, July 10, 2009 12:20 PM > Having two similar-but-slightly-different constructs in the language > (i.e. "subtype S is Pkg.T;" vs. "type S renames Pkg.T;") may be > confusing to some users at first, but is this any worse in this > respect than subtypes vs. derived types or qualified expressions vs. > type conversion? In fact, three constructs: subtype S is Pkg.T; type T is new Pkg.T; -- with null record when needed type T renames Pkg.T; -- whose semantics is not that different from the -- previous one. **************************************************************** From: Randy Brukardt Sent: Friday, July 10, 2009 1:50 PM > It is less general, but if it is powerful enough to solve the problems > that integrated packages were intended to solve, then that's all we > need. Wasn't that the original idea that eventually led to the integrated package idea? I thought we previously explored that possibility. I recall that Bob Duff was very much against it, claiming that there is lots of other stuff (like exceptions, and sometimes enumeration types) that need to come along with the primitive operations of a type. That's why we eventually gravitated to a package-based solution. Of course, it's worthwhile to look back at other solutions, because we still aren't happy with what we've got. But I'd expect the basic objections to remain. **************************************************************** From: Steve Baird Sent: Friday, July 10, 2009 2:11 PM What may have changed is the magnitude of the objections to the other available alternatives. If we conclude that integrated packages are a complicated mess (and I am not quite ready to concede that point), then alternatives that we originally discarded may start looking better. **************************************************************** From: Brad Moore Sent: Friday, July 10, 2009 5:29 PM > The first question that comes to my mind is whether there are problems > that need solving for which (some version > of) integrated packages provide a solution and this proposal does not. Well I can think of one other problem this would solve that the integrated packages solution does not solve. The issue about not being able to derive from a synchronized tagged when you want a primitive subprogram that returns an access to an element of the instantiation. With the ability to rename a type, you would not need to derive a new type. I think. Hopefully we would be allowed to complete an incomplete type with a renamed type. e.g. with Generic_Synchronized_List_Package; package Pkg is type List_Type; package Nested is type T is private; private type T is record List : access List_Type; end record; end Nested; type R renames Nested.T; package I is new Generic_Synchronized_List_Package (R); -- type List_Type is new I.List with null record; -- ERROR Cannot derive if I.List is a synchronized tagged type type List_Type renames I.List; -- This should work, hopefully end Pkg; **************************************************************** From: Tucker Taft Sent: Friday, July 10, 2009 9:09 PM The problem I see is that a package involves more than a type and its operations. A package defines nested generics, exceptions, objects, etc. As it is now, when you use the idiom of instantiating a package and then defining a derived type to get the operations to be direct components of the outer package, you have to know that the nested generics, etc., are not direct components, but still require the extra level of selection. In some way the renaming would help, in that you could use the nested generics and the objects without having to throw in some conversions to "undo" the type derivation. But that is really only solving half of the problem. I really think the ability to "integrate" a generic instantiation has a lot of value on its own. Being able to "integrate" an arbitrary package seems useful, but less so. Being able to integrate a directly declared nested package is definitely just a "trick" in my view, and I have become reinterested in the "end private" approach as preferable to the "package <> is ... end <>;" **************************************************************** From: Steve Baird Sent: Thursday, October 29, 2009 7:40 PM I have an action item from Brest to do something with this AI, so here we go. Based on the discussions since Brest, it seems that we have two major questions to resolve: 1) Anonymous vs. named integrated packages. Should the non-integrated view of an integrated package be nameable? Should we support both ways? 2) Implicit rename declarations vs. use-visibility. Do we want to (in some cases, at least) go with implicit renames, possibly with the addition of some rules to allow collisions between homographic renames of the same thing? As with any AI, there is also the question of "should we do nothing?". Doing nothing could only make sense if we adopt some other solution to the early instantiation problem. The current writeup of the AI has named integrated packages and use-visibility. As an alternative, I'll sketch out the opposite combination, anonymous integrated packages with implicit renames. I would like to reach a consensus regarding which direction we want to proceed with this one. I'm leaning towards named packages (when you make something anonymous, you discover later that you want to name it) with implicit rename declarations. ======== !proposal A package or package rename which is declared immediately within the visible part of an enclosing package may be declared with a non-identifier in place of the package identifier. This may be either "<>" or a bracketed identifier such as "". This indicates that the package is to be "integrated" into its parent; this means that clients of the parent have the ability to name the constructs declared within the integrated package as though they were declared within the enclosing package. The integrated package itself is anonymous and cannot be named, even from within itself. One scenario where this construct may be useful is in declaring a package that exports both a private type and a container generic instance for that type without requiring that clients deal with nested packages. This can be accomplished using this new construct as follows: with Some_Container_Generic; package Pkg is package <> is type T is private; private ...; end <>; package <> is new Some_Container_Generic (T); - exports type Valise end Pkg; with Pkg; package Client is X : Pkg.T; Y : Pkg.Valise; end Client; Two integrated packages declared within the same declarative region are required to have distinct non-identifiers unless at least one of the two declares a package rename or an instance of a generic package. This is needed in order to map a package body to its corresponding specification. The body for one of these guys cannot be a separate subunit. For each visible declaration in the visible part of an integrated package, there is an implicit rename declaration renaming that declaration immediately following the declaration of the integrated package. A type or subtype declaration is "renamed" via a constraintless subtype declaration. No rename is generated for an incomplete type declaration. Renames are generated for enumeration literals. Renames are generated for (visible) implicitly declared declarations. Renames of subprograms preserve default parameter values. A package rename may be integrated, in which case renames/subtypes are generated for the visible declarations of the renamed package. The dynamic semantics of a package declaration (e.g., its elaboration and finalization) are unaffected by whether or not it is integrated. Immediately after the implicit declarations associated with an integrated package, a check for violations of the no-homographs rule in the enclosing declarative region is performed. If a pair of illegal homographs is detected such that exactly one of the two is among the set of just-added implicit rename/subtype declarations, then that one is cast into the pit of darkness, never to be seen again (thereby averting the illegal homograph collision). If an implicit rename/subtype declaration survives this test, then a subsequent homograph conflict involving that declaration is a hard error. package P1 is Foo : Integer; package <> is Foo : Float; end <>; -- ok; P1.Foo is of type Integer end P1; package P2 is package <> is Foo : Float; end <>; Foo : Integer; -- illegal end P2; package P3 is package <1> is Foo : Integer; end <1>; package <2> is Foo : Float; end <2>; -- ok; P3.Foo is of type Integer end P1; An integrated package is treated like an unnamed block statement for purposes of Ada.Tags.Expanded_Name and friends. Renames are generated only for the declarations that are visible at the point of an integrated package rename declaration. Thus, package P is X : Integer; end P; package P.Child ...; end P.Child; with P; package Q is package <> renames P; -- implicitly declares Q.X end Q; with P.Child; with Q; package Q_Client is Y : Integer renames Q.X; -- ok package Goof renames Q.Child; -- illegal end Q_Client; !discussion Is there any reason to allow/disallow an integrated package declared immediately within another integrated package? There was discussion earlier of some sort of relaxed homograph checking mode which would allow homographic renames if they renamed the same thing. It appears that the "cast them into the pit of darkness" mechanism has eliminated the need for this. Do folks agree? The "renames the same thing" test might be useful in helping a compiler decide whether to emit a warning message about a discarded, but that's not part of the language definition. Alternatively, the rule stated above could be tightened up so that package P3 above would be illegal, but we would still allow package P4 is package <1> is C_E : exception renames Constraint_Error; end <1>; package <2> is C_E : exception renames Constraint_Error; end <2>; -- ok: P4.C_E denotes Constraint_Error end P4; The idea is that a declaration would be discarded (thereby averting an illegal homograph collision) only if the declaration that it conflicts with denotes the same thing (after looking through renames), as well as perhaps passing other tests (e.g., default parameters agree for a subprogram rename). This would result in less discarding, more homograph collisions, and perhaps more readable code. We probably want to disallow integrated renames of enclosing packages, just because they add complexity without adding utility. A limited view of a package doesn't see integrated packages at all; if it's good enough for instances ... **************************************************************** From: Tucker Taft Sent: Thursday, October 29, 2009 8:28 PM The idea of putting the identifier inside "<>" is very clever. It provides some additional interesting alternatives. In particular, it allows us to establish special rules for these identifiers. For example, we considered allowing them to be usable in expanded names, but never become directly "use" visible. **************************************************************** From: Randy Brukardt Sent: Thursday, October 29, 2009 8:57 PM ... > There was discussion earlier of some sort of relaxed homograph > checking mode which would allow homographic renames if they renamed > the same thing. It appears that the "cast them into the pit of > darkness" mechanism has eliminated the need for this. I would agree, but I don't think the "pit of darkness" model works in all cases. For instance, if you have two containers of different kinds, you're going to want to somehow get at both cursor types. But its quite possible that other operations that you care about wouldn't conflict, or would be redefined. So I think there needs to be a shorthand which allows renaming items that otherwise would end up in the "pit", without having to rename the entire contents of a potentially large package. (We talked about this privately, but since it has disappeared and it is a critical part of the model, IMHO.) For instance, consider something like: package Test is package <> is type T is private; ... end <>; package <> is new Ada.Containers.Vector (T, Natural); package <> is new Ada.Containers.Map (T, String); end Test; Here, the second type Cursor would disappear (or become illegal), and both probably is not what you want. Which would then force you back to the same old solutions that don't work very well. But if you could say something like: renames Cursor => Map_Cursor, No_Cursor => Map_No_Cursor, Then the problem goes away (everything else you want is overloadable). package Test is package <> is type T is private; ... end <>; package <> is new Ada.Containers.Vector (T, Natural); package <> is new Ada.Containers.Map (T, String) renames Cursor => Map_Cursor, No_Cursor => Map_No_Cursor; end Test; It also means that you can use a stricter legality model such as the alternative you outlined (which seems much less error prone - I don't like the idea that moving a declaration could cause something unrelated [and not clearly declared] to become invisible - isn't that a form of Beaujolais??). Other thoughts: perhaps we should require an identifier in the <>? One could imagine that identifier being available in local expanded names (that is, in the package body). And that would eliminate any problem with body matching and the like -- you could have as many of these as you want. --- > I'm leaning towards named packages (when you make something anonymous, > you discover later that you want to name it) I don't mind the idea of naming them, so long as the name is *never* visible outside of the package itself. Otherwise, you bring up all of the problems of controlling the client view (and the impossibility of maintenance that changes that view). **************************************************************** From: Brad Moore Sent: Saturday, October 31, 2009 7:23 PM > But if you could say something like: > renames Cursor => Map_Cursor, No_Cursor => Map_No_Cursor, > > Then the problem goes away (everything else you want is overloadable). > > package Test is > package <> is > type T is private; > ... > end <>; > > package <> is new Ada.Containers.Vector (T, Natural); > package <> is new Ada.Containers.Map (T, String) > renames Cursor => Map_Cursor, No_Cursor => Map_No_Cursor; > end Test; I had been thinking about this over the summer and arrived at something very similar to this as well. The idea of being able to supply renames, for types, enumerations, objects, etc to eliminate conflicts for anonymous packages. Another issue with generics in general that is addressed by this approach: I was also thinking that the names in a generic package have to be generic, but when you want to instantiate a generic, it would be nice to supply names that are more specific, so that the client can use names that more closely fit the abstraction. For example, instead of List, you might want to see Grocery_List, or List_Of_Ingredients. I arrived at a syntax for generating renames for a package. The idea was to use the <> as a place holder in the renames. This placeholder represents the original name from the package you are integrating. You could give renames for specific entities, or you could also apply this "wild card" approach. For the containers example, I might use: package Test is package <> is type T is private; ... end <>; package <> is new Ada.Containers.Doubly_Linked_List (T) with renames (Empty_List => Empty_Grocery_List, No_Element => No_Grocery_Element, Others => Grocery_<>); package <> is new Ada.Containers.Doubly_Linked_List (T) with renames (Empty_List => Empty_Ingredient_List, No_Element => No_Ingedient, Others => <>_Of_Ingredients); X : List_Of_Ingredients; Y : Grocery_List; end Test; In this model, I was thinking that there should be no pit of despair. Conflicts should not be allowed period, as hopefully this mechanism would provide sufficient flexibility to eliminate all conflicts. With this scheme, we probably would want to avoid using "renames" to do a package renames, because you might end up with two "renames" in the same declaration. In that case, I would suggest we not use the "renames" for the package renames, but just eliminate the word "new", as in package P is type T is null record; end P; with P; package Q is package <> is P with renames (Others => Cup_Of_<>ea); end Q; I think this helps, but ideally we could reduce the usage of <> as well. Perhaps "Use" could be used for the anonymous package rename, and reserver the <> for the rename wildcards. Or (dare I say) add a new reserved word such as "blend" which implies you are integrating something, and hopefully a word like blend is not too likely to conflict with existing usage. eg. package Test is blend package foo is type T is private; ... end foo; blend new Ada.Containers.Doubly_Linked_List (T) with renames (Empty_List => Empty_Grocery_List, No_Element => No_Grocery_Element, Others => Grocery_<>); -- or is "use" preferable? use new Ada.Containers.Doubly_Linked_List (T) with renames (Empty_List => Empty_Ingredient_List, No_Element => No_Ingedient, Others => <>_Of_Ingredients); X : List_Of_Ingredients; Y : Grocery_List; end Test; **************************************************************** From: Tucker Taft Sent: Saturday, October 31, 2009 8:03 PM Wouldn't it be simpler if you could just use a regular rename? E.g.: package is new Ada.Containers.Maps(T, Natural); subtype Map_Cursor is T_Maps.Cursor; No_Cursor : Map_Cursor renames T_Maps.No_Cursor; It seems like allowing the name to be used in certain circumstances, at least inside the package, is simpler than inventing more new syntax, such as for renaming. **************************************************************** From: Randy Brukardt Sent: Saturday, October 31, 2009 11:13 PM True, but that means that you have to *allow* what Steve called the "pit of despair", essentially names that are hidden from all visibility. As Brad mentions, that rule is better off not existing. The problem is that maintenance that added one or more declarations on one of the generic packages could cause declarations in some unrelated anonymous instance far away to disappear and cause errors. ("unrelated" other than accidental proximity.) The whole reason that I am pushing for these "integrated packages" to be anonymous is to avoid maintenance hazards. It doesn't help much to push the bump around (in that case, we're better off just living with the problems and the imperfect solutions). **************************************************************** From: Tucker Taft Sent: Sunday, November 1, 2009 10:02 AM Isn't this the same thing that happens with "use" visibility, namely that if you change one package spec, other packages also "use"d in the same scope will suddenly lose declarations? I don't see why this problem is any worse. Perhaps you could give a more concrete example to explain the problem. I also couldn't tell from your response why the proposal for using "regular" renames was related to this issue of "despair." **************************************************************** From: Brad Moore Sent: Sunday, November 1, 2009 11:11 AM I think the "regular" renames proposal almost works, but not quite. For this to work comparable to the renaming idea, I am thinking that; 1) If there are conflicts, all conflicting names are thrown into the pit of despair, not just the 2nd occurrence. 2) To get the code to compile, all conflicts need to be resolved by "fishing" things out of the pit of despair using renames, until there are no conflicts. Once you are down to a conflict between two uses, fishing one of the uses out of the pit, brings the remaining use out of the pit as well, because there no longer would be a conflict. 3) To fish a type name out of the pit of despair, would require a type rename. A subtype declaration doesn't cut it, because that doesn't fish out all the other things associated with the type. Unfortunately, type renames do not exist today. That is the missing piece of the puzzle I think. So I think you could accomplish the same ends using the "renaming approach" if we find a way to do type renames, or you could go with an approach along the lines of what Randy and I are suggesting, which gives you another way to get type renames. **************************************************************** From: Tucker Taft Sent: Sunday, November 1, 2009 11:33 AM I don't understand why a subtype declaration wouldn't work. Are you saying the type's primitive operations are somehow tied to whether the type's name conflicts? That seems weird. **************************************************************** From: Brad Moore Sent: Sunday, November 1, 2009 12:17 PM Maybe (more likely) I am not understanding something about the regular renaming idea. I was thinking that a subtype declaration would not count as a substitute for a type rename, because it doesn't give you things like enumeration literals, exceptions, class-wide operations. It's the same reason why we sometimes derive a new type (as a kludge) rather than use a subtype declaration. You could create renames for all of those as well, (or deriving new types) but then you might as well be doing the package integration all by hand at that point. It seems to me that once you start deriving new types, you no longer are integrating a package, but creating a new one. **************************************************************** From: Tucker Taft Sent: Sunday, November 1, 2009 1:01 PM Well maybe *I'm* missing something, but I presumed that everything in the "integrated" package was made visible in the enclosing scope, unless there was some kind of conflict. So all of those enumeration literals, exceptions, etc., have already been made visible in the enclosing scope, even if the type name happens to conflict. So a subtype declaration for the type name is all you need to do. All of the other things, presuming they didn't conflict, are already there, even if their type isn't directly nameable. **************************************************************** From: Brad Moore Sent: Sunday, November 1, 2009 1:23 PM True, but I am thinking conflicts for other things could end up being quite common. Suppose you have a package that wants to instantiate the same generic with two different types. The whole 2nd instance would end up being in the pit of despair, correct? Or suppose you have a set of container libraries that have similar names for objects (Like Cursor, No_Element, etc) so they can be used more interchangeably. (Like our standard containers) Two Container instantiations in the same package, even if they are different containers could end up conflicting quite a bit. I was thinking after I sent my last email that I should have included an example. So here it is. generic type F is tagged private; package P is -- Enumeration Literals type E is (This, That, Other_Thing); type T is new F with null record; function Process (Item : T) return Boolean; -- Classwide Operation function Compare (L, R : T'Class) return Boolean; -- Exception Red_Alert : exception; end P; -- My understanding of the renames approach with P; package Q is type T1 is null record; type T2 is null record; package <> is new P(T1); package is new P(T2); -- At this point all of the second instance is in the pit of despair, -- correct? subtype Foo_T2 is Foo.T; -- This gives me T, but about T'Class type Foo2_T2 is new Foo.T with null record; -- OK, now I have T'Class but had to derive a new type. -- I dont really have Foo.T type Foo_E is new Foo.E; Foo_Red_Alert : exception renames Foo.Red_Alert; -- The more declarations in P, the more renames we need. end Q; -- The approach I was suggesting. Or if you go with what I was suggesting, this becomes the following; Note that the following scales better than the above. The more declarations in P, might not involve adding any more declarations to the following with P; package Q is type T1 is null record; type T2 is null record; use new P(T1); use new P(T2) with renames (Others => Foo_<>); end Q; -- Example usage with Q; procedure Main is X : Q.T; Y : Q.Foo_T; Z : Q.Foo_E; begin case Z is when Q.This => null; when Q.That => null; when Q.Other_Thing => null; end case; end Hmmm. Would the enumeration literals resolve correctly to the correct version? I am thinking they would because Z is of a specific type. **************************************************************** From: Tucker Taft Sent: Sunday, November 1, 2009 1:49 PM This feels a bit like we are just "making stuff up." I think we should be looking for minimal syntax and semantic invention here. But back to our specific issue: I presume that things that are overloadable would not be considered conflicts, in the same way they don't conflict as far as use-visibility. Since we are trying to do away with having to use a type derivation and all the confusion that creates, the only thing that would ever need renaming that is *not* overloadable, among those things provided by a derived type, is the type name itself. You currently don't get exceptions, generics, etc. when you do a type derivation, so I believe my claim that a subtype declaration is just as good as a type "renaming" in this context is correct. And the integrated package is clearly better than type derivation because you *do* get exceptions, generics, etc., presuming they don't conflict. But if we start doing wholesale renaming of the entire contents to deal with conflicts of non- overloadables, then why bother? Using a subpackage name is probably just as convenient. I would say having two integrated instances of the same package in the same scope is not the primary purpose of this feature, and presuming you have some sort of a name for the integrated subpackages that can at least be used locally, then selective, "targeted" renaming is adequate to deal with that case. Wholesale renaming just doesn't make sense to me. **************************************************************** From: Randy Brukardt Sent: Monday, November 2, 2009 6:03 PM > Isn't this the same thing that happens with "use" visibility, namely > that if you change one package spec, other packages also "use"d in the > same scope will suddenly lose declarations? > I don't see why this problem is any worse. Perhaps you could give a > more concrete example to explain the problem. > > I also couldn't tell from your response why the proposal for using > "regular" renames was related to this issue of "despair." It is similar to the problem of "use" visibility, but it is worse because it is forced on clients by the design of the packages (rather than by their own use of "use"). Something I just realized this morning in thinking about this question (which I read last night but didn't answer due to the lateness of the hour) is that a major reason that I am mostly use-adverse is the maintenance hazard that "use" causes. That is, adding a declaration somewhere can make something unrelated disappear. I hadn't realized until today that this is why I don't mind "use" clauses of overloadable entities as much (especially operators and enumeration literals) -- they are far less likely to run into this "maintenance disappearance" because it only happens when the profiles are identical. (And that's pretty weird.) For non-overloadable entities, it happens even when the two items could never be confused by a human (such as an exception and a function). Currently, programmers can decide how much of this maintenance hazard they are willing to risk, by choosing how much to use "use" clauses and directly visible names, and how much to use fully-qualified names (FQNs). Even when you do have a use clause, you can avoid the maintenance hazard by using an FQN. But that isn't true if you have Steve's "pit of despair", any version. The name disappears even when you do use an FQN, since the integrated package name isn't available for use (and that itself is important for maintenance, so that clients cannot depend on whether an integrated package is used, or a set of direct declarations, or a set of renames of a nested package -- they all work the same to a client -- allowing the implementation to be changed if necessary). Let me try to give a simple example: generic package G1 is Foo : Integer; --Bar : Integer; -- (1) end G1; with G1; package Test is package is new G1; package is Bar : Float; -- (2) end ; end Test; with Test; procedure Main is function Is_Groddy (I : Integer) return Boolean is ... function Is_Groddy (F : Float) return Boolean is ... begin if Is_Groddy (Test.Bar) then -- (3) ... end Main; As written, (3) resolves to the float Bar declared at (2). Let's look at what happens if the declaration of Bar at (1) is added. With Steve's original proposal, which is order-based, the Bar at (1) becomes automatically renamed because of the integrated package . Then, because a homograph already exists when integrating , the Bar at (2) is cast into the pit of despair (which really means it is hidden from all visibility, or even more simply, it isn't declared). The problem here is that we have a first-class Beaujolias effect: the call at (3) changes silently from using object (2) to using object (1). (Of course, it is more likely to cause some sort of error, but I wanted to show the worst case.) I believe that is unacceptable, so I won't spend any more time on Steve's first idea. Another idea is to revert to use-like visibility for integrated packages. That is, *both* names Bar are tossed into the pit of despair and neither is visible in Test (this has strange semantics, because after and before , there is a point where Bar is directly visible in Test, and then is later disappears). That makes the name Test.Bar at (3) illegal, because there are two homographs meaning that neither is visible. I find this obnoxious, because the change is completely unrelated to the package where the error occurred. The writer of this code doesn't care at all about the potion of Test; moreover the maintainer of G1 probably doesn't know that users of Test will be impacted by a seemingly unrelated change. Also note that the writer of Main tried to avoid exactly this sort of effect by not using a use-clause -- yet they end up with one of the worst side-effects of use clauses simply because some other author used integrated packages. Note that in this case there is no obvious repair. We can't easily change anything to make Bar properly visible. If we have Tucker's suggested local names for these packages, we could rename the two Bars to something else, but of course that would force all of the clients to be changed. I suppose with a complex enough definition of what happens to these homographs we might be able to allow an explicit renaming to Bar (that is Bar : Float renames N1.Bar;), but that seems pretty complex to me. The rule I was advocating was that if there are conflicting homographs, then the program is simply illegal. Steve notes that is necessary anyway when there are explicit declarations of things, so I'm actually suggesting a simpler rule: no conflicts. Then the maintenance hazard is at least closer to the original cause. In that case, adding Bar to G1 would make package Test illegal. Now, we need a way to work around that restriction when it unavoidably happens (such as when two different containers are instantiated). That's why I suggested supporting renaming as part of integrated packages. If you had added Bar to G1, all you would need to do to make Test legal again (and not change any of Test's clients) would be to add an appropriate rename. Using the syntax that Brad suggested (a bit better than mine): with G1; package Test is package is new G1 with renames Bar => Soap_Bar; package is Bar : Float; end ; end Test; And now there aren't any homographs and the program is legal. In addition, no clients need to change. Note that this renaming facility is mainly aimed at non-overloadable entities, so we don't need any profile information. This also means that the name does not need to be visible outside of the package itself, which seems to be a simpler rule to me than trying to allow it to be used in its parent unit (only). It avoids having to answer the question as to whether N1 is visible in a child of Test, for instance. Anyway, much food for thought. I've got to get all of the work done to prepare for this meeting, so I can't spend any more time discussing this until I see you on Thursday. **************************************************************** From: Randy Brukardt Sent: Monday, November 2, 2009 7:35 PM > But that isn't true if you have Steve's "pit of despair", any version ... Steve points out that the correct term is "pit of darkness". I apparently was too tired on Saturday night to actually remember the proper term. "despair" is probably a bit much for this issue (although it does seem appropriate in some ways :-). Sorry about any confusion. ****************************************************************