!standard 4.1.3(12) 09-05-26 AI05-0135-1/04 !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 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). 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? 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: 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: Randy Brukardt Sent: Tuesday, May 26, 2009 6:38 PM **************************************************************** From: Randy Brukardt Sent: Tuesday, May 26, 2009 6:38 PM **************************************************************** From: Randy Brukardt Sent: Tuesday, May 26, 2009 6:38 PM **************************************************************** From: Randy Brukardt Sent: Tuesday, May 26, 2009 6:38 PM ****************************************************************