Rationale for Ada 2005
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.
© 2005, 2006 John Barnes Informatics.
Sponsored in part by: