Rationale for Ada 2005: Structure and visibility

RUSTOP
BACKNEXT

ENG

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

  1        with ...
  2        package P is
  3                ... -- visible specification
  4        end;
  5        with ...
  6        package private P is -- just dreaming
  7                ... -- private part
  8        end;
  9        with ...
 10        package body P is
 11                ... -- body
 12        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

  1        package App is
  2                ...
  3        private
  4                ...
  5        end App;
  6
  7        package App.Pub is
  8                ...
  9        private
 10                ...
 11        end App.Pub;
 12
 13        private package App.Priv is
 14                ...
 15        private
 16                ...
 17        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

  1        private with Q;
  2        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

  1        package P is
  2                ...
  3                with Q; -- we cannot write this
  4        private
  5                ...
  6        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

  1        private with App.Priv;
  2        package App.Pub is
  3                ... -- App.Priv not visible here
  4        private
  5                ... -- App.Priv visible here
  6        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

  1        limited private with App.Priv;
  2        package App.Pub is
  3                ... -- App.Priv not visible here
  4        private
  5                ... -- limited view of App.Priv here
  6        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

  1        limited with App.Pub; limited private with App.Priv;
  2        package App is
  3                ... -- limited view of App.Pub here
  4        private
  5                ... -- limited view of App.Priv here
  6        end App;

@ A simple example of the use of private with clauses was given in the Introduction. Here it is somewhat extended

  1        limited with App.User_View; limited private with App.Secret_Details;
  2        package App is
  3                ... -- limited view of type Outer visible here
  4        private
  5                ... -- limited view of type Inner visible here
  6        end App;
  7        private package App.Secret_Details is
  8                type Inner is ...
  9                ... -- various operations on Inner etc
 10        end App.Secret_Details;
 11        private with App.Secret_Details;
 12        package App.User_View is
 13                type Outer is private;
 14                ... -- various operations on Outer visible to the user
 15                -- type Inner is not visible here
 16        private
 17                -- type Inner is visible here
 18                type Outer is
 19                        record
 20                                X : Secret_Details.Inner;
 21                                ...
 22                        end record;
 23                ...
 24        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

  1        private with Q;
  2        package P is
  3                ... -- Q not visible here
  4        private
  5                use Q;
  6                ... -- use visibility of Q here
  7        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

  1        package P is
  2                function F return Integer;
  3        end P;
  4        function F return Integer;
  5        with P;
  6        private with F;
  7        package Q is
  8                use P;
  9                X : Integer := F;   -- illegal
 10                Y : Integer := P.F; -- legal
 11        private
 12                Z : Integer := F;   -- legal, calls the library F
 13        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

  1        private with P; pragma Elaborate(P);
  2        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

  1        with P; with P; with P, P;
  2        package Q is ...

@ To avoid complexity we similarly allow

  1        with P; private with P;
  2        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

  1        private with P;
  2        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

  1        with P; -- full view
  2        limited with P; -- limited view
  3        private with P; -- full view from private part
  4        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.

Rationale for Ada 2005: Structure and visibility

@ENGRUSTOPBACKNEXT

3. Видимость из приватных секций

@ Ада 95 представляет публичные и приватные дочерние пакеты чтобы иметь возможность подсистемам анализироваться структурированным способом. Основная идея состоит в том что публичные потомки допускают декомпозицию представления, а частные потомки допускают декомпозицию реализации подсистемы пользователю подсистемы.

@ В свою очередь и у публичных и приватных потомков могут быть собственные потомки обоих видов. Это хорошо работало в большинстве случаев, но трудность возникла относительно приватных частей.

@ Утверждение, что приватная часть пакета действительно касается реализации пакета вместо того, чтобы определить средства внешнему пользователю. Хотя это не касается алгоритмических аспектов реализации, это действительно касается реализации абстракции данных. Во время первоначального дизайна Ады преобладала идея, что пакет должен быть написан и откомпилирован как три отдельных части. Возможно так:

  1        with ...
  2        package P is
  3                ... -- visible specification
  4        end;
  5        with ...
  6        package private P is -- just dreaming
  7                ... -- private part
  8        end;
  9        with ...
 10        package body P is
 11                ... -- body
 12        end;

@ Каждая часть, возможно, даже имела бы свое собственное контекстное выражение, как видим.

@ Однако, было ясно, что это будет настоящим административным кошмаром во многих ситуациях.

@ Это было несомненно правильным решением вообще. Разделение на две части хорошо поддерживает раздельную трансляцию и хотя частная часть не является частью логического интерфейса пользователю, это действительно предоставляет информацию о физическом интерфейсе, и это необходимо компилятору.

@ Проблема, которая появилась, состоит в том, что частная часть публичного пакета не может обратиться к информации в частных дочерних пакетах. Частные потомки конечно не видимы пользователю, но нет никакой причины, почему они не должны быть видимы из частной части публичного пакета при условии, что так или иначе информация не просачивается. Таким образом, рассмотрим иерархию:

  1        package App is
  2                ...
  3        private
  4                ...
  5        end App;
  6
  7        package App.Pub is
  8                ...
  9        private
 10                ...
 11        end App.Pub;
 12
 13        private package App.Priv is
 14                ...
 15        private
 16                ...
 17        end App.Priv;

@ Нет никакой причины почему приватные секции App и App.Pub, а так же видимая часть спецификации App.Priv не должны совместно использовать видимость (приватная часть App.Priv логически принадлежит более глубокому уровню сокрытия тайны). Но это совместное использование не возможно на Аде 95.

@ Публичному пакету App.Pub не разрешено иметь with выражение для дочернего пакета App.Priv, так как это означало бы что видимая часть App.Pub также имела бы видимость этой информации, и посредством механизма, такого как переименование могло быть передано внешнему пользователю.

@ Спецификации родительского пакета App также не разрешается иметь with выражение для App.Priv, так как это нарушило бы правила зависимости так или иначе. У любого потомка есть зависимость от своего родителя и таким образом родительская спецификация должна быть откомпилирована или грузиться из библиотеки раньше.

@ Заметим, что приватная часть публичного дочернего App.Pub действительно автоматически имеет видимость приватной части родительского App. Но обратное не верно из-за правил зависимости.

@ Обратите внимание, что дочерний пакет App.Priv может иметь with выражение для своего публичного собрата App.Pub (это создает зависимость конечно), но который только дает приватную дочернюю видимость видимой части публичного потомка.

@ Таким образом, единственная совместная видимость среди этих трех областей на Аде 95 состоит в том, что приватная часть публичного потомка и видимая часть приватного потомка могут видеть приватную часть родителя.

@ Практическое последствие этого состоит в том, что в больших системах, информация, которая должна быть располжена ниже по иерархии должна быть помещена в приватную часть исходного родителя. Это приводит к тому, что родительский пакет чрезмерно разрастается, создавая, таким образом, трудности при обслуживании и провоцирует частые перекомпиляции родителя и, таким образом, целой иерархии пакетов.

@ Ситуация значительно облегчилась в Аде 2005 за счёт введения limited with выражений.

@ Если у пакета P есть приватное with выражение для пакета Q как показано ниже:

  1        private with Q;
  2        package P is ...

@ тогда у приватной части P есть видимость видимой части пакета Q, тогда как у видимой части P нет видимости Q и, таким образом, видимость не может быть передана пользователю P. Это похоже на то, как будто with выражение было присоединено только к приватной части P следующим образом:

  1        package P is
  2                ...
  3                with Q; -- we cannot write this
  4        private
  5                ...
  6        end P;

@ Это повторяет предыдущую декомпозицию пакета состоящую из 3-х частей, обсуждённую вначале.

@ Приватное with выражение может быть помещено везде, куда и нормальное with выражение для упомянутых модулей и, кроме того, приватное with выражение, которое упоминает, что приватный модуль может быть помещено в любой из потомков его родителя.

@ Таким образом, мы можем поместить приватное with выражение для App.Priv в App.Pub, таким образом разрешающей видимость приватного потомка от приватной части ее публичного собрата. Таким образом:

  1        private with App.Priv;
  2        package App.Pub is
  3                ... -- App.Priv not visible here
  4        private
  5                ... -- App.Priv visible here
  6        end App.Pub;

@

Это работает, если мы не сталкиваемся с правилами зависимости. Private with выражение означает, что у общественного потомка есть зависимость от приватного потомка, и поэтому приватный потомок должен быть откомпилирован или загрузиться из библиотеки сначала.

@ Мы могли бы получить ситуацию, когда существует взаимная зависимость между публичным и приватным собратом, в котором у каждого есть тип, который хочет обратиться к другому. В таком случае мы можем использовать limited with выражение следующим образом:

  1        limited private with App.Priv;
  2        package App.Pub is
  3                ... -- App.Priv not visible here
  4        private
  5                ... -- limited view of App.Priv here
  6        end App.Pub;

@ Дочерние пакеты и зависят от родительского пакета и, таким образом, родитель не может иметь with выражения для них. Но у родителя может быть limied with выражение для публичног потомка и limited private with выражение для приватного потомка следующим образом:

  1        limited with App.Pub; limited private with App.Priv;
  2        package App is
  3                ... -- limited view of App.Pub here
  4        private
  5                ... -- limited view of App.Priv here
  6        end App;

@ Простой пример использования private with выражений был дан во Введении. Здесь это несколько расширено:

  1        limited with App.User_View; limited private with App.Secret_Details;
  2        package App is
  3                ... -- limited view of type Outer visible here
  4        private
  5                ... -- limited view of type Inner visible here
  6        end App;
  7        private package App.Secret_Details is
  8                type Inner is ...
  9                ... -- various operations on Inner etc
 10        end App.Secret_Details;
 11        private with App.Secret_Details;
 12        package App.User_View is
 13                type Outer is private;
 14                ... -- various operations on Outer visible to the user
 15                -- type Inner is not visible here
 16        private
 17                -- type Inner is visible here
 18                type Outer is
 19                        record
 20                                X : Secret_Details.Inner;
 21                                ...
 22                        end record;
 23                ...
 24        end App.User_View;

@ В предыдущей секции мы заметили, что были проблемы со взаимодействиями между use выражениями, неограниченными with выражениями, и limited with выражениями. Те правила также относятся private with выражениям, где private with выражение обрабатывается как неограниченное with выражение, и limited private with выражение обработано как limited with выражение. Другими словами, private проигнорирован с целью тех правил.

@ Кроме того, мы не можем поместить use выражение пакета в то же самое выражение контекста как private with выражение (limited или не). Это потому что мы тогда ожидали бы, что это будет относиться к видимой части также, которая будет неправильной. Однако, мы можем всегда помещать use выражение в частную часть следующим образом:

  1        private with Q;
  2        package P is
  3                ... -- Q not visible here
  4        private
  5                use Q;
  6                ... -- use visibility of Q here
  7        end P;

@ Рискуя тем, чтобы смутить читателя стоить указать, что, строго говоря, правила относительно private with обработаны как правила законности, а не правила видимости. Вот пример, который иллюстрирует эту тонкость и опасности, которых это избегает:

  1        package P is
  2                function F return Integer;
  3        end P;
  4        function F return Integer;
  5        with P;
  6        private with F;
  7        package Q is
  8                use P;
  9                X : Integer := F;   -- illegal
 10                Y : Integer := P.F; -- legal
 11        private
 12                Z : Integer := F;   -- legal, calls the library F
 13        end Q;

@ Если мы обработали правила относительно private with тем, поскольку чистая видимость постановляет тогда, что вызов F в объявлении X в видимой части был бы запросом P.F. Так перемещение объявления X к приватной части тихо изменило бы F, называемый - это будет противно. Мы можем всегда писать запрос F как P.F как показано в объявлении Y.

@ Таким образом, правила относительно private with написаны, чтобы сделать объекты видимыми, но неприличными в видимой части. На практике программисты могут только обработать их как правила видимости так, чтобы объекты не были видимы вообще, который является, как мы описали их выше.

@ Полезное последствие неприличного, а не невидимого подхода - то, что мы можем использовать название пакета, упомянутого в limited with выражении в прагме в выражении контекста таким образом:

  1        private with P; pragma Elaborate(P);
  2        package Q is ...

@ Private with выражениям фактически позволены на телах также, когда они только ведут себя как нормальные with выражением. Другой незначительный пункт - то, что Ада всегда разрешала несколько with выражений для того же самого модуля в одном выражении контекста таким образом:

  1        with P; with P; with P, P;
  2        package Q is ...

@ Чтобы избежать сложности, мы так же позволяем:

  1        with P; private with P;
  2        package Q is

@ и затем limited with проигнорировано.

@ Мы ввели limited with выражения в этой секции как решение проблемы доступа к частным дочерним записям от частной части родительского или общественного брата. Но у них есть другое важное использование. Если мы имеем:

  1        private with P;
  2        package Q is ...

@ тогда мы уверены, что пакет Q не может неосторожно обратиться к P в видимой части и, в частности передать доступ к объектам в P посредством переименования и так далее. Таким образом, написание private with предоставляет дополнительную документационную информацию, которая может быть полезной и для человеческих рецензентов и программировать инструментальные средства анализа. Так, если у нас есть ситуация, где private with выражение есть все, что необходимо тогда, чтобы мы использовали его, а не нормальное with выражение.

@ Заметим, тогда как в Аде 95 есть только одна форма with выражения, в Аде 2005 имеется аж четыре формы:

  1        with P; -- full view
  2        limited with P; -- limited view
  3        private with P; -- full view from private part
  4        limited private with P; -- limited view from private part

@ Наконец, отметим, что, если private with выражение дано в спецификации тогда, оно относится к телу так же как частной части.

@ ENG RUS

TOP BACK NEXT

2010-10-24 00:26:54

. .