Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.

For the best experience please use the latest Chrome or Safari browser. Firefox 10 (to be released soon) will also handle it.

Ada Language Update for Corrigendum 2014

Notification

2014年6月時点の話です。 最終的にどうなるかはわかりません。 筆者はヲチしてるだけで関係者ではありません。 勘違い及び私見や願望も含まれております。

全体目次 (1/4)

☞ Ada 2005 の復習
  Ada 2012 の復習
  Ada Corrigendum 2014
  未解決のIssues

Ada 2005 の復習

またの名を Amendment 1 to Ada 95。

Ada 2005 を三行で

世間の情報はほとんどAda 95のもの。
2005以降を知らずにAdaを語るなかれ。
とはいえ2005以降を解説している日本語ページは皆無なので英語読んでください。

全体目次 (2/4)

  Ada 2005 の復習
☞ Ada 2012 の復習
  Ada Corrigendum 2014
  未解決のIssues

Ada 2012 の復習

構文糖、構文糖、構文糖。

Ada 2012 を三行で

構文糖。
構文糖。
個人的には微妙な構文糖も多いです。

全体目次 (3/4)

  Ada 2005 の復習
  Ada 2012 の復習
☞ Ada Corrigendum 2014
  未解決のIssues

Ada Corrigendum 2014

Ada Corrigendum 2014はAda 2012の細かい修正。
次の規格自体はAda 202xになる。

raise expression

AI12-0022-1
A := raise Program_Error;

式の中で例外を投げられる。

raise expression

既にAda 2012で、if、case、forは式中でも使えた。

B := (if ... then ... else ...);

C := (case ... is when ... => ... when ... => ...);

 

D := (for all I in ARR'Range => CHK (ARR (I)));
--  CHK (ARR (1)) and then CHK (ARR (2)) and then ...

E := (for some I in ARR'Range => CHK (ARR (I)));
--  CHK (ARR (1)) or else CHK (ARR (2)) or else ...

endがいらない代わりに括弧は必要。

raise expression

function Based_Log (Base, X : Float) return Float is
begin
   return (if Base <= 0.0 or else Base = 1.0 then
         raise Argument_Error;
      else
         Log2 (X) / Log2 (Base));
end Based_Log;

あまり嬉しくなさそう?
普通のif文でいいよね。

raise expression

どこで使えばいいのか?

raise expression

事前条件。

raise expression

先のBased_Logの仕様部。

function Based_Log (Base, X : Float) return Float;

何を渡せば例外になるのかわからない。

raise expression

そこで、Ada 2012のPre aspectを使って事前条件を表明。

function Based_Log (Base, X : Float) return Float
   with Pre => Base > 0.0 and then Base /= 1.0;

仕様部を見ただけで渡すべき引数がわかる。

raise expression

しかし、この書き換えは問題を生む。
投げる例外が、明記していたArgument_Errorから事前条件が失敗した時に投げられるAssertion_Errorに変わってしまう。

raise expression

raise expressionで解決。

function Based_Log (Base, X : Float) return Float
   with Pre => (Base > 0.0 and then Base /= 1.0)
      or else raise Argument_Error;

raise expression

本体側もスッキリ。

function Based_Log (Base, X : Float) return Float is
begin
   return Log2 (X) / Log2 (Base);
end Based_Log;

raise expression

DBCの本家Eiffelで書くとすると……チェックに名前を付けることができ、この名前が例外の種類になる。

Based_Log (Base, X : REAL) : REAL
   require
      Argument_Error : Base > 0.0 and then Base /= 1.0;

Eiffelでは、DBCと例外メカニズムは一体のもの。
ここを真似てない劣化コピーが多すぎる。

Predicate(_Failure)

似たような話。
従来(Ada 2005まで)は、連続した範囲のsubtypeは作れても

subtype Positive is Integer range 1 .. Integer'Last;
subtype Negative is Integer range Integer'First .. -1;

ゼロ以外は正負OK、みたいな途中抜かしはできなかった。

subtype Non_Zero is Integer
   range Integer'First .. -1 | 1 .. Integer'Last; -- error!

Predicate(_Failure)

Ada 2012では、subtypeの制約に自由な式を書けるようになった。

subtype Non_Zero is Integer
   with
      Static_Predicate =>
         Non_Zero in Integer'First .. -1 | 1 .. Integer'Last;

静的に評価できるものはStatic_Predicate、それ以外はDynamic_Predicateと使い分ける。

Predicate(_Failure)

predicateも、投げられる例外はAssertion_Error

X : Non_Zero := 0; -- Assertion_Error

事前条件と似たような問題。
ちなみに従来のsubtypeの範囲エラーはConstraint_Error

Predicate(_Failure)

raise expressionでOKでは?

subtype Non_Zero is Integer
   with
      Static_Predicate =>
         (Non_Zero in Integer'First .. -1 | 1 .. Integer'Last)
         or else raise Constraint_Error;

……これでいいと思われていた。

Predicate(_Failure)

subtypeの用途はいろいろある。例えば条件式でも使える。

if X in Non_Zero then -- 例外投げちゃダメ
   ...
else
   ... -- X = 0の時はここが実行されないと
end if;

predicateの式で例外投げちゃダメ!

Predicate(_Failure)

AI12-0054-2

predicate失敗時に例外を投げる場所が別に用意された。

subtype Non_Zero is Integer
   with
      Static_Predicate =>
         (Non_Zero in Integer'First .. -1 | 1 .. Integer'Last)
      Predicate_Failure =>
         raise Constraint_Error;

(predicate式中のraise expressionは文脈によってはFalse扱い案もあったがPredicate_Failureが勝利)

Predicate(_Failure)

この話はまだ続く。
ところで、Dynamic_Predicateには何でも書けるわけですが……

ん?今何でも(ry

Predicate(_Failure)

procedure Read_A_From_File (File : File_Type)
   with Pre => Is_Open (File)
      and then Mode (File) = In_File;
procedure Read_B_From_File (File : File_Type)
   with Pre => Is_Open (File)
      and then Mode (File) = In_File;
procedure Read_C_From_File (File : File_Type)
   with Pre => Is_Open (File)
      and then Mode (File) = In_File;

開いている状態のファイルを渡して欲しいが……
事前条件だと、並ぶと結構くどい。

Predicate(_Failure)

subtype Open_File_Type is File_Type
   with
      Dynamic_Predicate =>
         Is_Open (Open_File_Type),
      Predicate_Failure =>
         raise Status_Error;

ファイルが開いている状態のsubtypeとか……

Predicate(_Failure)

subtype Input_File_Type is Open_File_Type
   with
     Dynamic_Predicate =>
        Mode (Input_File_Type) = In_File,
     Predicate_Failure =>
        raise Mode_Error;

subtype Output_File_Type is Open_File_Type
   with -- (省略)

更に入力/出力モードで限定するとか……

Predicate(_Failure)

procedure Read_A_From_File (File : Input_File_Type);
procedure Read_B_From_File (File : Input_File_Type);
procedure Read_C_From_File (File : Input_File_Type);

すっきり。

Predicate(_Failure)

ええぇ!?それってsubtypeじゃなくね?
理論的に考えて。

Predicate(_Failure)

細けえことはいいんだよ!
(Open_File_Type/Input_File_Type/Output_File_Typeは実際にCorrigendum 2014のドラフトに載ってますマジで)

Predicate(_Failure)

だからこんなif文も

if Is_Open (File) then
   ...

こう書き換える事もできる。

if File in Open_File_Type then
   ...

チェックがしたいだけならOpen_File_Type (File)と型変換するだけでもOK。
何でもかんでもsubtype。

Predicate(_Failure)

procedure Read_A_From_File (File : Input_File_Type);
procedure Read_B_From_File (File : Input_File_Type);
procedure Read_C_From_File (File : Input_File_Type);

例えばRead_A_From_FileRead_B_From_Fileを呼ぶとき、事前条件では呼び出し毎に毎回チェックがなされるが、subtypeでは同じInput_File_Typeなのでチェックも省かれることが期待できる。
チェックが必要なのは最初にFile_TypeからInput_File_Typeになる時の1回だけ。
(もちろんコンパイラの機嫌が良ければ)

Cのvariadic function in 64bit

variadic functionってのは可変長引数のこと。
要するにprintfのこと。

Cのvariadic function in 64bit

use Interfaces;
function printf (format: C.Strings.chars_ptr)
   return C.int;
function printf (format: C.Strings.chars_ptr; a1 : C.int)
   return C.int;
pragma Import (C, printf);

今まではこんな風にしてprintfを呼べていた。
ところが64bit環境下ではクラッシュする!

Cのvariadic function in 64bit

64bit環境下ではクラッシュする!ナンデ!?

System V AMD64 ABIでは、eaxに浮動小数点数の引数の数を入れないといけない。
でたらめな値が入っていると、その数のXMMレジスタをどうこうしようとしてクラッシュする。

Cのvariadic function in 64bit

C言語からでも自分でプロトタイプ宣言を書く場合

int printf (char const *, ...);

は動くが、...を省いた

int printf (char const *);

はクラッシュする!
(警告は出ます)

Cのvariadic function in 64bit

なので、インラインアセンブラでeaxをゼロクリア。

System.Machine_Code.Asm ("xorl %eax, %eax");
printf ("X = %d" & lf & nul, X);

これで一応動く。
……最適化の機嫌次第で。

Cのvariadic function in 64bit

AI12-0028-1

pragma Importで使える呼び出し規約にC_Variadic_Nが追加される。
(Nは固定の引数の数)

use Interfaces;
function printf (format: C.Strings.chars_ptr)
   return C.int;
function printf (format: C.Strings.chars_ptr; a1 : C.int)
   return C.int;
pragma Import (C_Variadic_1, printf);

CPU_Set

AI12-0033-1

System.Multiprocessors.Dispatching_Domainsにタスク毎のCPUの範囲を限定する機能が入る。
4コアの場合、タスクAは1 or 2、タスクBは3 or 4を使え等。

Pure packageでの裏技の禁止

package Pure_Package is
   pragma Pure;
   ...
end Pure_Package;

packageをPureにすると、色々嬉しい。

Pure packageでの裏技の禁止

packageをPureにすると、グローバル変数、Pureではないpackageの使用が禁止される。
これにより、型がRemote Call Interfaceに使えるようになったりもする。
関数が__attribute__((pure))扱いになったりもする。

Pure packageでの裏技の禁止

ところで、C++で言うところのmutableを実現するテクニックとしてこういうのがある。

type T is record
   Mutable : access T := T'Access;
   Var : Integer;
end record;

デフォルト値として自分自身への非constantなアクセスを獲得しておく。

X.Mutable.Var := 10;

XがconstantであってもMutable経由で変更できる。

Pure packageでの裏技の禁止

Pureなpackage内でこれをやると、グローバル変数が存在できてしまっていた!

package Pure_Package is
   pragma Pure;

   X : constant T := (
      Mutable => <>, -- デフォルト値を使用
      Var => 0);

end Pure_Package;

このPure_Package.Xはconstantなのに変更可能!

Pure packageでの裏技の禁止

AI12-0076-1

昔っからの話ながら、ようやく禁止されます。
めでたしめでたしなのかは不明。

他にも

accessibility checkまわりの修正。
Type_Invariantまわりの修正。
RPCまわりの修正。
pragmaからaspectへの移行の漏れ修正。
構文の曖昧なところを修正。
他、微調整多数。

全体目次 (4/4)

  Ada 2005 の復習
  Ada 2012 の復習
  Ada Corrigendum 2014
☞ 未解決のIssues

未解決のIssues

前のAda 2012 Language Updateスライドを見てくださった方は、アレどうなったの?という疑問を覚えていて……くれてませんよね……。

(自意識過剰)

未解決: コンテナを入れ子にできない

stuct T {
  vector<T> children;
};

別にC++でも規格で保証されているわけではないが、実装によってはできる。最悪BoostでもOK。
Adaでは絶対できない。

未解決: コンテナを入れ子にできない

type T; -- この時点でTはincomplete type
package T_Vectors is new Vectors (T); -- エラー!
type T is record -- Tの実体
   children : T_Vectors.Vector;
end record;

record型の宣言とgeneric packageのインスタンス化の構文が別になっているため、recordの宣言の途中でインスタンス化できない。
Ada 2012ではincomplete type(Cのopaque type)をgenericで使えるようにはなったが、結局Vectorsは完全な型の情報を必要とするため無力。

未解決: コンテナを入れ子にできない

AI05-0011-1 AI05-0074-\* AI05-0151-1 AI05-0162-1 AI05-0213-1

色々案は出た。

with private, limited new, private new, end private, pragma May_Be_Partial ……

……が、全部没ったorz

未解決: コンテナを入れ子にできない

代わりに木構造だけを実現するAda.Containers.Multiway_Treesが2012で入って、Corrigendum 2014でも細々微修正されたりする。
そんなのJ節送りにしてこっち進めてくださいよう……。

(※Annex JはObsolescent Features)

未解決: “Integrated” nested packages

AI05-0135-\*

要するにDの public import 、OCamlの include
まともなモジュール機能を持つAdaでは、#includeのネストみたいなことができない。
更にpackageが小分けになる傾向のあるAdaでは、with節やrenamesを大量にダラダラ並べる羽目になる。
そうして提案された“Integrated” nested packages は絶対欲しい機能……だった。(過去形)

未解決: “Integrated” nested packages

激しい議論の末、決まらないまま2012年になってしまった。タイムアップ。
AI12- として再開される様子も無く絶賛凍結中。

新しく出てきたIssues

……の中で、これは欲しい、というやつ。

提案: Generic formal record types

AI12-0019-1
generic
   type Rec is record
      Next : access Rec;
   end record;
package Intrusive_Lists is
   ...

特定の要素を持つrecordを要求するgeneric。

提案: Generic formal record types

type T is record
   Next_In_The_List : access T;
   Next_In_Another : access T;
end record;
package List_1 is
   new Intrusive_Lists (T (Next => Next_In_The_List));
package List_2 is
   new Intrusive_Lists (T (Next => Next_In_Another));

インスタンス化の際に別の名前にもできる。

提案: 配列のaggregate式中で添字の使用

AI12-0061-1
Data : array (1 .. 10) of Integer := (
   for I in 1 .. 5 => I,
   for I in 6 .. 10 => 12 - I);

↑と↓は同じになる。

Data : array (1 .. 10) of Integer := (
   1, 2, 3, 4, 5, 6, 5, 4, 3, 2);

提案: 配列のaggregate式中で添字の使用

Mapped : array (Data'Range) of Integer := (
   for I in Data'Range => Data (I) * 2);

お手軽なmap関数の代わりにも。

提案: constexpr

AI12-0075-1

Ada 2012で追加されたfunction expressionをconstexpr扱いにする。

function Bitwidth (X : Integer) return Integer is
   (if X <= 1 then 0 else Bitwidth ((X + 1) / 2) + 1);
type T is mod 10;
for T'Size is Bitwidth (10); -- コンパイル時に計算

Adaではsubtypeの範囲や配列のサイズ等の決定は実行時まで遅らせてよいので実はC++程の需要は無い。
とはいえ'Size属性等、静的に決める必要があるものも少しはある。コードサイズの削減にもなる。
それになにより、constexprですよconstexpr!

おしまい

ご清聴ありがとうございました。

Thanks to ななかなn+ε号機さん (@n7k7)

Use a spacebar or arrow keys to navigate