Стандарт языка Ада 2012 был опубликован, как официальный документ ISO/IEC 8652:2012, в декабре 2012 года. В феврале 2016 был опубликован стандарт с техническими правками ISO/IEC 8652:2012/Cor 1:2016. Этот последний документ и определяет действующий стандарт языка Ада.
С момента публикации началась работа над следующей версией языка. Будущий стандарт условно называется Ada 202x. Окончательное название и дата публикации пока не известны.
Черновик стандарта можно посмотреть на сайте ada-auth.org. А подробную информацию о ходе разработки новой версии можно почерпнуть на странице Ada Rapporteur Group. Там же можно найти подробности по каждому отдельному рассмотренному вопросу AI12-XXXX-X.
Что же будет в новом стандарте Ады? Вот, с моей точки зрения, наиболее интересные нововведения.
Много новых возможностей связаны с упрощением распараллеливанием программ.
AI12-0119-1 AI12-0251-1
Вводится новое ключевое слово parallel. Оно используется в написании двух новых операторов.
Параллельный блок:
procedure Traverse (T : Expr_Ptr) is begin if T /= null and then T.all in Binary_Operation'Class -- see 3.9.1 then -- recurse down the binary tree parallel do Traverse (T.Left); and Traverse (T.Right); and Ada.Text_IO.Put_Line ("Processing " & Ada.Tags.Expanded_Name (T'Tag)); end do; end if; end Traverse;
Параллельный цикл:
parallel for I in Grid'Range(1) loop Grid(I, 1) := (for all J in Grid'Range(2) => Grid(I,J) = True); end loop;
Для параллельного цикла можно задать количество параллельно исполняемых отрезков указав его как выражение либо как спецификацию индекса:
parallel (Num_CPUs) for I in Arr'Range loop A(I) := B(I) + C(I); end loop; declare Partial_Sum : array (1 .. Num_CPUs) of Integer := (others => 0); begin parallel (Chunk in Partial_Sum'Range) for I in Arr'Range loop -- Какие-то сложные манипуляции Partial_Sum (Chunk) := @ + some_complicated_computation; end loop; Sum := Partial_Sum'Reduce ("+", 0); end;
AI12-0262-1 AI12-0242-1
Техника программирования Map-Reduce широко распространена. (Подробнее.) В место того, чтобы писать подпрограммы и циклы, необходим другой механизм при котором входные и промежуточные значения существуют в виде потока значений. Это позволит задействовать все параллельные устройства машины. Вводится новый атрибут Reduce:
V'Reduce(Reducer, Initial_Value[, Combiner])
Где в качестве V может выступать одно из
value_sequence ::= '[' [parallel[(chunk_specification)]] iterated_component_association ']'
В итоге программа переберет все элементы V и начальное значение Initial_Value выполнит над ними подпрограмму Reducer, которая может быть функцией или процедурой:
function Reducer (Accumulator : Accum_Type; Value : Value_Type) return Accum_Type; procedure Reducer (Accumulator : in out Accum_Type; Value : in Value_Type);
Вот пример вычисления факториала таким образом:
function Factorial(N : Natural) return Natural is ([for J in 1 .. N => J]'Reduce("*", 1));
AI12-0064-2
Чтобы компилятор мог использовать параллельность по полной нужно гарантировать некоторые свойства программы. К ним относится наличие блокировок по ходу исполнения кода.
На этапе описания подпрограммы можно указать, что она не содержит блокирующих операций. Работает и на уровне пакетов:
package Ada.Calendar with Nonblocking is type Time is private;
AI12-0079-1
Другим важным свойством связанным с распаралелливанием является отсутствие побочных эффектов исполнения кода. Для их описания вводится новый аспект Global. Синтаксис его не так прост. Можно
procedure X with Global => (in null, -- нет побочных эффектов out all); -- любые побочные эффекты procedure Y with Global => (in private of My_Package, out access Integer);
AI12-0234-1 AI12-0321-1
Для упрощения реализации неблокирующих алгоритмов добавили
Пример использования:
declare type Atomic_Boolean is new Boolean with Atomic; package Exchange is new Atomic_Operations.Exchange (Atomic_Type => Atomic_Boolean); Lock : aliased Atomic_Boolean := False; begin -- Obtain the lock while Exchange.Atomic_Exchange (Item => Lock, Value => True) loop null; end loop ... -- do stuff Lock := False; -- Release the lock end;
В эту группу попадают несколько изменений.
AI12-0250-1
Во всех конструкциях, где используются итераторы, теперь можно добавлять фильтр — условия при срабатывании которого элементы участвуют в итерации, а остальные пропускаются.
for J in 1 .. 10 when J mod 2 = 0 loop Ada.Text_IO.Put_Line (J'Img); end loop;
AI12-0189-1
Вводится новый способ итерации по контейнеру. Контейнер определяет процедуру итерации принимающую на вход указатель на подпрограмму. Эта подпрограмма затем вызывается для нужных элементов контейнера. Чтобы упростить использование, вводится новый вид циклов. Тело такого цикла компилятор преобразует в анонимную подпрограмму и передаёт ссылку на неё в итератор.
package Maps is ... procedure Iterate (Container : in Map; Process : not null access procedure (Key : in Key_Type; Element : in Element_Type)); end Maps My_Map : Maps.Map; for (Key, Value) of My_Map.Iterate loop Put_Line (Key_Type'Image (Key) & " => " & Element_Type'Image (Value)); end loop;
AI12-0266-1
Для контейнерных типов также возможны параллельные циклы. Чтобы они работали для вашего собственного контейнера нужно предоставить итератор специального вида:
type Parallel_Iterator is limited interface and Forward_Iterator; subtype Chunk_Index is Positive; function Is_Split (Object : Parallel_Iterator) return Boolean is abstract; procedure Split_Into_Chunks (Object : in out Parallel_Iterator; Max_Chunks : Chunk_Index) is abstract; function Chunk_Count (Object : Parallel_Iterator) return Chunk_Index is abstract; function First (Object : Parallel_Iterator; Chunk : Chunk_Index) return Cursor is abstract; function Next (Object : Parallel_Iterator; Position : Cursor; Chunk : Chunk_Index) return Cursor is abstract;
Авторы языка стараются сделать его более выразительным и удобным. Сюда относится литералы для пользовательских типов, агрегаты для контейнеров, новые виды агрегатов для массивов и записей.
AI12-0249-1 AI12-0295-1
Новый аспект позволяет пользователю указать функцию преобразования числового литерала в данный тип. Это позволяет избежать явного вызова этой функции, например:
type Big_Integer is private with Integer_Literal => Big_Integer_Value; function Big_Integer_Value (S : String) return Big_Integer; ... Y : Big_Integer := -3; -- Что эквивалентно записи: -- Y : Big_Integer := - Big_Integer_Value ("3");
Аналогично аспект Real_Literal используется для дробных литералов, а String_Literal для строковых.
AI12-0061-1
Теперь можно будет писать так
G : constant Matrix := (for I in 1 .. 4 => (for J in 1 .. 4 => (if I = J then 1.0 else 0.0)));
AI12-0212-1
Появилась возможность инициализировать контейнеры с помощью агрегатов специального вида. Поддерживаются почти все стандартные контейнеры (кроме Multiway_Trees) и определенные пользователем контейнеры, для которых указаны специальные аспекты. Для записи агрегата используются квадратные скобки:
X : My_Set := [1, 2, 3];
Что равнозначно:
X : My_Set := Empty_Set; Include (X, 1); Include (X, 2); Include (X, 3);
Соответственно, если вы определяете собственный контейнер, вы можете указать какими операциями его инициализировать:
type Set_Type is private with Aggregate => (Empty => Empty_Set, Add_Unnamed => Include); function Empty_Set return Set_Type; procedure Include (S : in out Set_Type; N : Element);
Для контейнеров типа ключ‐значение используется агрегат с именованным сопоставлением:
M := [12 => "house", 14 => "beige"];
Внутри контейнеров можно использовать итераторы. Есть также сокращенная форма, когда не нужно отдельно записывать значение ключа, например:
M := [for Key of Keys use Key => Integer'Image (Key)]; -- сокращенная форма: M := [for Key of Keys => Integer'Image (Key)];
Агрегат с квадратными скобками можно использовать и для массивов. Им проще записывать массивы без элементов и с одним элементом, т.к. нет необходимости указывать индекс:
Keys : constant array (Positive range <>) of Integer := [2, 3, 5, 7]; K1 : constant array (Positive range <>) of Integer := [2]; K0 : constant array (Positive range <>) of Integer := []; -- Для Ады 2012: K1 : constant array (Positive range <>) of Integer := (1 => 2); K0 : constant array (Positive range <>) of Integer := (1 .. 0 => <>);
AI12-0127-1
Новая форма агрегата для замены части компонент новыми значениями. Удобно использовать с постусловиями:
procedure The_Answer (V : in out Vector; A, B : in Integer) with Post => V = (V'Old with delta A .. B => 42.0, V'First => 0.0);
AI12-0020-1
Теперь атрибуты Image, Wide_Image и Wide_Wide_Image работают для всех типов. Пользователь может определить результат этих атрибутов для своих сложных типов определив аспект Put_Image предоставив процедуру преобразования.
type T is record ... end record with Put_Image => T_Image; procedure T_Image (Arg : T; Stream : not null access Ada.Streams.Root_Stream_Type'Class);
AI12-0125-3
Вводится сокращение в операторах присваивания для обозначение левой части.
Board (1, 1) := @ + 1; -- An abbreviation for Board (1, 1) := Board (1, 1) + 1;
AI12-0275-1
При переименовании объекта можно опустить тип, так как он известен и так.
declare X renames Get_My_Position.X; begin X := X + 1; end;
AI12-0236-1
Для удобства записи контрактов вводится новый вид выражений
declare_expression ::= declare {declare_item} begin body_expression
Например,
with Dynamic_Predicate => (declare Q : constant Integer := H(S.A, S.B); R : constant Integer := G(S.C, S.D); begin (if W(Q, R) then F(Q) else Z(R)))
AI12-0272-1
Для подпрограмм параметров настройки можно задавать пре/пост условия, а для типов — аспект Default_Initial_Condition.
generic type Element is private with Default_Initial_Condition => Is_Empty (Element); with function Is_Empty (Value : Element) return Boolean; with function Hash (Value : Element) return Hash_Type with Post => Hash'Result in 0 .. 99; package Maps is
AI12-0208-1
Два новых пакета
Множество вопросов носит технический либо узкоспециализированный характер. Для полноты приведу здесь лишь список таких изменений.
На этом пока всё.
Август 2018 — Декабрь 2018