Rationale for Ada 2005: Structure and visibility

RUSTOP
BACKNEXT

ENG

4. Aggregates

@ There are important changes to aggregates in Ada 2005 which are very useful in a number of contexts. These were triggered by the changes to the rules for limited types which are described in the next section, but it is convenient to first consider aggregates separately.

@ The main change is that the box notation <> is now permitted as the value in a named aggregate.

@ The meaning is that the component of the aggregate takes the default value if there is one.

@ So if we have a record type such as

  1        type RT is
  2                record
  3                        A : Integer := 7;
  4                        B : access Integer;
  5                        C : Float;
  6                end record;

@ then if we write

  1        X : RT := (A => <>, B => <>, C => <>);

@ then X.A has the value 7, X.B has the value null and X.C is undefined. So the default value is that given in the record type declaration or, in the absence of such an explicit default value, it is the default value for the type. If there is no explicit default value and the type does not have one either then the value is simply undefined as usual.

@ The above example could be abbreviated to

  1        X : RT := (others => <>);

@ The obvious combinations are allowed

  1        (A => <>, B => An_Integer'Access, C => 2.5)
  2        (A => 3, others => <>)
  3        (A => 3, B | C => <>)

@ The last two are the same. There is a rule in Ada 95 that if several record components in an aggregate are given the same expression using a | then they have to be of the same type. This does not apply in the case of <> because no typed expression is involved.

@ The <> notation is not permitted with positional notation. So we cannot write

  1        (3, <>, 2.5) -- illegal

@ But we can mix named and positional notations in a record aggregate as usual provided the named components follow the positional ones, so the following are permitted

  1        (3, B => <>, C => 2.5)
  2        (3, others => <>)

@ A minor but important rule is that we cannot use <> for a component of an aggregate that is a discriminant if it does not have a default. Otherwise we could end up with an undefined discriminant.

@ The <> notation is also allowed with array aggregates. But in this case the situation is much simpler because it is not possible to give a default value for array components. Thus we might have

  1        P : array (1.. 1000) of Integer := (1 => 2, others => <>);

@ The array P has its first component set to 2 and the rest undefined. (Maybe P is going to be used to hold the first 1000 prime numbers and we have a simple algorithm to generate them which requires the first prime to be provided.) The aggregate could also be written as

  1        (2, others => <>)

@ Remember that others is permitted with a positional array aggregate provided it is at the end. But otherwise <> is not allowed with a positional array aggregate.

@ We can add others => <> even when there are no components left. This applies to both arrays and records.

@ The box notation is also useful with tasks and protected objects used as components. Consider

  1        protected type Semaphore is ... ;
  2        type PT is
  3                record
  4                        Guard    : Semaphore;
  5                        Count    : Integer;
  6                        Finished : Boolean := False;
  7                end record;

@ As explained in the next section, we can now use an aggregate to initialize an object of a limited type. Although we cannot give an explicit initial value for a Semaphore we would still like to use an aggregate to get a coverage check. So we can write

  1        X : PT := (Guard => <>, Count => 0, Finished => <>);

@ Note that although we can use <> to stand for the value of a component of a protected type in a record we cannot use it for a protected object standing alone.

  1        Sema : Semaphore := <>; -- illegal

@ The reason is that there is no need since we have no coverage check to concern us and there could be no other reason for doing it anyway.

@ Similarly we can use <> with a component of a private type as in

  1        type Secret is private;
  2        type Visible is
  3                record
  4                        A : Integer;
  5                        S : Secret;
  6                end record;
  7        X : Visible := (A => 77; S => <>);

@ but not when standing alone

  1        S : Secret := <>; -- illegal

@ It would not have any purpose because such a variable will take any default value anyway.

@ We conclude by mentioning a small point for the language lawyer. Consider

  1        function F return Integer;
  2        type T is
  3                record
  4                        A : Integer := F;
  5                        B : Integer := 3;
  6                end record;
  7

@ Writing

  1        X : T := (A => 5, others => <>); -- does not call F

@ is not quite the same as

  1        X : T; -- calls F
  2        ...
  3        X.A := 5; X.B := 3;

@ In the first case the function F is not called whereas in the second case it is called when X is declared in order to default initialize X.A. If it had a nasty side effect then this could matter. But then programmers should not use nasty side effects anyway.

Rationale for Ada 2005: Structure and visibility

@ENGRUSTOPBACKNEXT

4. Агрегаты

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

@ Основное изменение состоит в том, что замкнутый блок <> теперь разрешен как значение в именованном агрегате.

@ Он определяет то, что компонент агрегата берет значение по умолчанию, если он имеется.

@ Так, если у нас имеется некий тип записи:

  1        type RT is
  2                record
  3                        A : Integer := 7;
  4                        B : access Integer;
  5                        C : Float;
  6                end record;

@ тогда если мы напишем:

  1        X : RT := (A => <>, B => <>, C => <>);

@ тогда X.A имеет значение 7, X.B - пустой указатель, а X.C не определенено. Таким образом, значеним по умолчанию назначается то, которое определяется в объявлении типа записи или, в отсутствии явного значения по умолчанию, это - значение по умолчанию для типа. Если нет никакого явного значения по умолчанию, и у типа нет одного ни одним тогда, значение просто неопределенно как обычно.

@ Вышеупомянутый пример мог быть сокращен к:

  1        X : RT := (others => <>);

@ Очевидные комбинации возможны:

  1        (A => <>, B => An_Integer'Access, C => 2.5)
  2        (A => 3, others => <>)
  3        (A => 3, B | C => <>)

@ Последние два - эквивалентны. В Аде 95 есть правило, что, если нескольким компонентам записи в агрегате дают одно и то же выражение, используя символ | тогда, они должны иметь одинаковый тип. Это не применяется в случае <>, потому что никакое напечатанное выражение не вовлечено.

@ Нотация <> запрещена с позиционным назначением. Т.е. мы не можем написать:

  1        (3, <>, 2.5) -- illegal

@ Но мы можем смешать именованный и позиционный способ назначения в рекордном агрегате как обычно, если именованные компоненты следуют за позиционными, таким образом:

  1        (3, B => <>, C => 2.5)
  2        (3, others => <>)

@ Небольшое, но важное правило состоит в том, что мы не можем использовать <> для компонента агрегата, который является дискриминантом, если у этого нет значения по умолчанию. Иначе мы могли закончить с неопределенным дискриминантом.

@ Нотация <> также разрешена с составными регулярными значениями. Но в этом случае ситуация намного более проста, потому что не возможно дать значение по умолчанию для компонентов массива. Таким образом, мы могли бы написать:

  1        P : array (1.. 1000) of Integer := (1 => 2, others => <>);

@ Первый элемент массива P имеет значение 2, а остальные неопределены. (Возможно P собирается использоваться, чтобы держать первые 1000 простых чисел, и у нас есть простой алгоритм, чтобы генерировать их, который требует, чтобы первое главное было предоставлено). агрегат мог также быть написан как:

  1        (2, others => <>)

@ Помните, что others разрешаетя с позиционным составным регулярным значением, если это в конец. Но иначе <> не разрешен с позиционным составным регулярным значением.

@ Мы можем добавить другие => <>, даже когда нет никаких компонентов слева. Это относится и к массивам и к записям.

@ Нотация <> также полезна с задачами и защищенными объектами, используемыми как компоненты. Рассмотрим:

  1        protected type Semaphore is ... ;
  2        type PT is
  3                record
  4                        Guard    : Semaphore;
  5                        Count    : Integer;
  6                        Finished : Boolean := False;
  7                end record;

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

  1        X : PT := (Guard => <>, Count => 0, Finished => <>);

@ Отметим, что хотя мы можем использовать <> чтобы заместить значение компонента защищенного типа в записи, мы не можем использовать его для защищенного объекта, стоящего одиночно.

  1        Sema : Semaphore := <>; -- illegal

@ Причина состоит в том, что нет никакой потребности, так как у нас нет никакой проверки охвата, чтобы коснуться нас и не могло быть никакой другой причины для выполнения этого так или иначе.

@ Так же мы можем использовать <> с компонентом приватного типа как в:

  1        type Secret is private;
  2        type Visible is
  3                record
  4                        A : Integer;
  5                        S : Secret;
  6                end record;
  7        X : Visible := (A => 77; S => <>);

@ но не стоящий одиночно:

  1        S : Secret := <>; -- illegal

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

@ Мы закончим небольшим курьёзным моментом. Рассмотрим:

  1        function F return Integer;
  2        type T is
  3                record
  4                        A : Integer := F;
  5                        B : Integer := 3;
  6                end record;
  7

@ Запись:

  1        X : T := (A => 5, others => <>); -- does not call F

@ не совсем тоже самое что и:

  1        X : T; -- calls F
  2        ...
  3        X.A := 5; X.B := 3;

@ В первом случае функция F не вызывается, тогда как во втором случае она вызывается при объявлении X для вычисления значения по умолчанию для X.A. Если бы у этого был противный побочный эффект тогда, то это могло бы иметь значение. Но тогда программисты не должны использовать противные побочные эффекты так или иначе.

@ ENG RUS

TOP BACK NEXT

2010-10-24 00:26:54

. .