Rationale for Ada 2005

John Barnes
Contents   Index   References   Search   Previous   Next 

4.3 Visibility from private parts

Ada 95 introduced public and private child packages in order to enable subsystems to be decomposed in a structured manner. The general idea is that 
public children enable the decomposition of the view of a subsystem to the user of the subsystem,
private children enable the decomposition of the implementation of a subsystem. 
In turn both public and private children can themselves have children of both kinds. This has proved to work well in most cases but a difficulty has arisen regarding private parts.
Recall that the private part of a package really concerns the implementation of the package rather than specifying the facilities to the external user. Although it does not concern algorithmic aspects of the implementation it does concern the implementation of data abstraction. During the original design of Ada some thought was given to the idea that a package should truly be written and compiled as three distinct parts. Perhaps like this 
with ...
package P is
   ...    -- visible specification
end;
with ...
package private P is    -- just dreaming
   ...    -- private part
end;
with ...
package body P is
   ...    -- body
end;
Each part could even have had its own context clause as shown.
However, it was clear that this would be an administrative nightmare in many situations and so the two-part specification and body emerged with the private part lurking at the end of the visible part of the specification (and sharing its context clause).
This was undoubtedly the right decision in general. The division into just two parts supports separate compilation well and although the private part is not part of the logical interface to the user it does provide information about the physical interface and that is needed by the compiler.
The problem that has emerged is that the private part of a public package cannot access the information in private child packages. Private children are of course not visible to the user but there is no reason why they should not be visible to the private part of a public package provided that somehow the information does not leak out. Thus consider a hierarchy
package App is
   ...
private
   ...
end App;
package App.Pub is
   ...
private
   ...
end App.Pub;
private package App.Priv is
   ...
private
   ...
end App.Priv;
There is no reason why the private parts of App and App.Pub and the visible part of the specification of App.Priv should not share visibility (the private part of App.Priv logically belongs to the next layer of secrecy downwards). But this sharing is not possible in Ada 95.
The public package App.Pub is not permitted to have a with clause for the child package App.Priv since this would mean that the visible part of App.Pub would also have visibility of this information and by mechanisms such as renaming could pass it on to the external user.
The specification of the parent package App is also not permitted to have a with clause for App.Priv since this would break the dependence rules anyway. Any child has a dependence on its parent and so the parent specification has to be compiled or entered into the program library first.
Note that the private part of the public child App.Pub does automatically have visibility of the private part of the parent App. But the reverse cannot be true again because of the dependence rules.
Finally note that the private child App.Priv can have a with clause for its public sibling App.Pub (it creates a dependence of course) but that only gives the private child visibility of the visible part of the public child.
So the only visibility sharing among the three regions in Ada 95 is that the private part of the public child and the visible part of the private child can see the private part of the parent.
The practical consequence of this is that in large systems, information which should really be lower down the hierarchy has to be placed in the private part of the ultimate parent. This tends to mean that the parent package becomes very large thereby making maintenance more difficult and forcing frequent recompilations of the parent and thus the whole hierarchy of packages.
The situation is much alleviated in Ada 2005 by the introduction of private with clauses.
If a package P has a private with clause for a package Q thus 
private with Q;
package P is ...
then the private part of P has visibility of the visible part of the package Q, whereas the visible part of P does not have visibility of Q and so visibility cannot be transmitted to a user of P. It is rather as if the with clause were attached to just the private part of P thus 
package P is
   ...
with Q;    -- we cannot write this
private
   ...
end P;
This echoes the three-part decomposition of a package discussed above.
A private with clause can be placed wherever a normal with clause for the units mentioned can be placed and in addition a private with clause which mentions a private unit can be placed on any of its parent's descendants.
So we can put a private with clause for App.Priv on App.Pub thereby permitting visibility of the private child from the private part of its public sibling. Thus 
private with App.Priv;
package App.Pub is
   ...    -- App.Priv not visible here
private
   ...    -- App.Priv visible here
end App.Pub;
This works provided we don't run afoul of the dependence rules. The private with clause means that the public child has a dependence on the private child and therefore the private child must be compiled or entered into the program library first.
We might get a situation where there exists a mutual dependence between the public and private sibling in that each has a type that the other wants to access. In such a case we can use a limited private with clause thus
limited private with App.Priv;
package App.Pub is
   ...    -- App.Priv not visible here
private
   ...    -- limited view of App.Priv here
end App.Pub;
The child packages are both dependent on the parent package and so the parent cannot have with clauses for them. But a parent can have a limited with clause for a public child and a limited private with clause for a private child thus 
limited with App.Pub; limited private with App.Priv;
package App is
   ...    -- limited view of App.Pub here
private
   ...    -- limited view of App.Priv here
end App;
A simple example of the use of private with clauses was given in the Introduction. Here it is somewhat extended 
limited with App.User_View; limited private with App.Secret_Details;
package App is
   ...    -- limited view of type Outer visible here
private
   ...    -- limited view of type Inner visible here
end App;
private package App.Secret_Details is
   type Inner is ...
   ...     -- various operations on Inner etc
end App.Secret_Details;
private with App.Secret_Details;
package App.User_View is
   type Outer is private;
   ...    -- various operations on Outer visible to the user
   -- type Inner is not visible here
private
   -- type Inner is visible here
    type Outer is
      record
         X: Secret_Details.Inner;
         ...
      end record;
   ...
end App.User_View;
In the previous section we observed that there were problems with interactions between use clauses, nonlimited with clauses, and limited with clauses. Those rules also apply to private with clauses where a private with clause is treated as a nonlimited with clause and a limited private with clause is treated as a limited with clause. In other words private is ignored for the purpose of those rules.
Moreover, we cannot place a package use clause in the same context clause as a private with clause (limited or not). This is because we would then expect it to apply to the visible part as well which would be wrong. However, we can always put a use clause in the private part thus 
private with Q;
package P is
   ...    -- Q not visible here
private
   use Q;
   ...    -- use visibility of Q here
end P;
At the risk of confusing the reader it might be worth pointing out that strictly speaking the rules regarding private with are treated as legality rules rather than visibility rules. Here is an example which illustrates this subtlety and the dangers it avoids 
package P is
   function F return Integer;
end P;
function F return Integer;
with P;
private with F;
package Q is
   use P;
   X: Integer := F;    -- illegal
   Y: Integer := P.F;    -- legal
private
   Z: Integer := F;    -- legal, calls the library F
end Q;
If we treated the rules regarding private with as pure visibility rules then the call of F in the declaration of X in the visible part would be a call of P.F. So moving the declaration of X to the private part would silently change the F being called – this would be nasty. We can always write the call of F as P.F as shown in the declaration of Y.
So the rules regarding private with are written to make entities visible but unmentionable in the visible part. In practice programmers can just treat them as visibility rules so that the entities are not visible at all which is how we have described them above.
A useful consequence of the unmentionable rather than invisible approach is that we can use the name of a package mentioned in a private with clause in a pragma in the context clause thus 
private with P;  pragma Elaborate(P);
package Q is ...
Private with clauses are in fact allowed on bodies as well, in which case they just behave as a normal with clause. Another minor point is that Ada has always permitted several with clauses for the same unit in one context clause thus 
with P;  with P;  with P, P;
package Q is ...
To avoid complexity we similarly allow 
with P;  private with P;
package Q is
and then the private with is ignored.
We have introduced private with clauses in this section as the solution to the problem of access to private children from the private part of the parent or public sibling. But they have other important uses. If we have 
private with P;
package Q is ...
then we are assured that the package Q cannot inadvertently access P in the visible part and, in particular, pass on access to entities in P by renamings and so on. Thus writing private with provides additional documentation information which can be useful to both human reviewers and program analysis tools. So if we have a situation where a private with clause is all that is needed then we should use it rather than a normal with clause.
In summary, whereas in Ada 95 there is just one form of with clause, Ada 2005 provides four forms 
with P;    -- full view
limited with P;    -- limited view
private with P;    -- full view from private part
limited private with P;    -- limited view from private part
Finally, note that if a private with clause is given on a specification then it applies to the body as well as to the private part.

Contents   Index   References   Search   Previous   Next 
© 2005, 2006, 2007 John Barnes Informatics.
Sponsored in part by:
The Ada Resource Association and its member companies: ARA Members AdaCore Polyspace Technologies Praxis Critical Systems IBM Rational Sofcheck and   Ada-Europe:
Ada-Europe