Ada 202x (16日目) - static function =================================== .. post:: Dec 16, 2019 :tags: ada, ada_2022 12月は31日までありますので折り返しは今日でした。 目玉を持ってくるならここですよね。 (意味不明。) static ------ staticという単語は様々な使われ方がされています。 Adaでは「コンパイル時に決まる」静的な値のことです。 決して可視性の意味でも寿命の意味でもクラスメソッドの意味でもRTTI不要という意味でもリンク時の何かでもありません。 規格だけで使われていた単語だったのですが、Ada 2012で ``Static_Predicate`` アスペクトが追加されたためソースコードにも出現するようになりました。 これは静的に検証可能な述語を持つrefinement typeを作るためのもので、動的にしか検証できない ``Dynamic_Predicate`` と対になっています。 ``Static_Predicate`` の方はcase文等でソースコード上に全パターン書かないといけない場合等に反映されます。 (``Dynamic_Predicate`` でもコンパイル時にチェック可能なケースでかつコンパイラの機嫌が良ければ警告ぐらいは出してもらえるかもしれません。 GNATには独自拡張で文脈から静的か動的かをコンパイラが判断する ``Predicate`` もあります。) .. container:: gray-and-small <読み飛ばし推奨> 「predicate type」と書いてしまいますとCecil等にあるアレの方が相応しいらしいです。 ですのでここではrefinement typeと書いています。 これに限らず規格にとっての静的とコンパイラにとっての静的というのは別な話です。 実際のコンパイラは規格で決まっているよりも多くのことを静的に解決できますが、規格上staticな値が要求されている箇所に規格上静的ではないとされる式があったらエラーにしないといけません。 (C言語でもインライン関数の結果を ``enum`` の値にできたりしないのと同じです。) 経緯 ---- Adaとは非常に動的な言語です。 (あくまで静的な型を持つ中では、です。) 定数、範囲型の範囲、固定長配列の要素数、 ``generic`` の引数、等々、広範に渡って実行時の値を用いることができます。 そのためメタプログラミングが他ほどは必要ありません。 それでもstaticな値が必要な箇所が少しはあります。 主にセマンティクスや内部表現に関わる部分です。 ``'Enum_Rep`` が必要な理由も ``'Size`` 属性を動的に変えられないからでした。 staticな値を作るには関数は使えず式だけで書いてしまうしかありませんでした。 そのためAda 2012でif式が入るまでは ``Boolean'Pos (Condition) * True_Value + Boolean'Pos (not Condition) * Else_Value`` 等といった冗長なコードも時には書かれていました。 最近のAdaは式中で分岐やループもできますのでこのようなコードは必要ないです。 それでも ``Ada.Numerics.Complex_Types.Complex_Type`` の演算子も呼べないため複素数の足し算ひとつ取っても実数部と虚数部を別々に足さなければなりません。 冗長なだけで済めばまだしも関数が呼べないと不可能なこともあります。 特に抽象型の値を使うことは全くできませんでした。 抽象型はパッケージの外からはサブプログラムを通じてしか操作できないようになっています。 大事な特徴なのですが、それ故にstaticには扱えません。 そしてアドレスを表す型の ``System.Address`` は抽象型でした。 つまりコンパイル時にアドレス演算ができずpreelaboratedなパッケージ(実行時に追加の初期化を必要としない翻訳単位)でメモリマップドI/Oに用いるアドレスを定数として定義できませんでした。 これはAdaの用途から致命的でしたので各処理系で様々な拡張がなされてきました。 GNATにも処理系定義でpragma ``Allow_Integer_Address`` 、``'To_Address`` 属性、 ``'Deref`` 属性等が用意されています。 | https://gcc.gnu.org/onlinedocs/gnat_rm/Pragma-Allow_005fInteger_005fAddress.html | https://gcc.gnu.org/onlinedocs/gnat_rm/Attribute-To_005fAddress.html | https://gcc.gnu.org/onlinedocs/gnat_rm/Attribute-Deref.html Janus/Adaでは ``System.Address`` は抽象型ではないため生の整数値を渡せるようです。 Ada 202xでの改善 ---------------- Staticアスペクト ++++++++++++++++ ``Static`` アスペクトを指定したexpression functionは引数もstaticであればコンパイル時の展開が保証されstaticな値を作るために使えるようになりました。 .. code-block:: ada function Add (X, Y : Integer) return Integer with Static is (X + Y); 制限もいくつかあります。 - expression functionのみです。 ``begin`` ``end`` を使用した普通の関数はstaticにはできません。 - bodyにはなれません。 公開部に実装も書かないといけないため抽象型の中身には触れないことになります。 - 再帰呼び出しはできません。 - 引数のモードは ``in`` のみです。 - 引数と返値の型はstatic subtype(数値型や列挙型等のスカラー型または文字列型)でなければなりません。 実行時の値を使った範囲等の制約や ``Dynamic_Predicate`` は持てません。 - 事前条件や事後条件は書けません。 - 返値の型が ``Type_Invariant`` を持っていては駄目です。 - 当たり前ですが非staticな式は使えません。 結局のところ現段階では制限のため抽象型 ``System.Address`` の操作をstaticにすることはできませんでした。 そのためワークアラウンド的に ``Ada.Unchecked_Conversion`` 、 ``System.Address_To_Access_Conversions`` 、 ``System.Storage_Elements`` の各関数は特別扱いされてpreelaboratedなパッケージでアドレス定数を定義できるようになりました。 需要の高さが伺えます。 制限はいずれ緩められていくことでしょう。 きっと。 多分。 いや知りませんけど。 関連AI ------ - `AI12-0075-1`_ **Static expression functions** - `AI12-0175-1`_ **Preelaborable packages with address clauses** 所感 ---- いずれは複素数やアドレスの演算が完全に静的になることでしょう。 …。 ……。 はい、はい、おっしゃりたいことはわかります。 とんでもない変更ですよね! これ! 現段階では再帰ができませんし使える型も限られていますが紛れもなく ``constexpr`` です。 コンパイル時プログラミングの道が開けたぜヒャッハー!! .. _`AI12-0175-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0175-1.txt .. _`AI12-0075-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0075-1.txt