Ada 202x (16日目) - static function¶
12月は31日までありますので折り返しは今日でした。 目玉を持ってくるならここですよね。 (意味不明。)
static¶
staticという単語は様々な使われ方がされています。 Adaでは「コンパイル時に決まる」静的な値のことです。
決して可視性の意味でも寿命の意味でもクラスメソッドの意味でもRTTI不要という意味でもリンク時の何かでもありません。
規格だけで使われていた単語だったのですが、Ada 2012で Static_Predicate
アスペクトが追加されたためソースコードにも出現するようになりました。
これは静的に検証可能な述語を持つrefinement typeを作るためのもので、動的にしか検証できない Dynamic_Predicate
と対になっています。
Static_Predicate
の方はcase文等でソースコード上に全パターン書かないといけない場合等に反映されます。
(Dynamic_Predicate
でもコンパイル時にチェック可能なケースでかつコンパイラの機嫌が良ければ警告ぐらいは出してもらえるかもしれません。
GNATには独自拡張で文脈から静的か動的かをコンパイラが判断する Predicate
もあります。)
<読み飛ばし推奨> 「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
属性等が用意されています。
Janus/Adaでは System.Address
は抽象型ではないため生の整数値を渡せるようです。
Ada 202xでの改善¶
Staticアスペクト¶
Static
アスペクトを指定したexpression functionは引数もstaticであればコンパイル時の展開が保証されstaticな値を作るために使えるようになりました。
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
です。
コンパイル時プログラミングの道が開けたぜヒャッハー!!