Ada 202x (21日目) - user-defined literal ======================================== .. post:: Dec 21, 2019 :tags: ada, ada_2022 息継ぎがしたいので寄り道回入れていいですか。 と言いましても割と大きな変更になります。 経緯 ---- これまで標準の型をラップするライブラリを使うのが結構大変でした。 Adaでは暗黙の型変換は匿名のaccess型まわりとユーザー定義参照型ぐらいしかありません。 定数ひとつ作るにも関数呼び出しが必要です。 .. code-block:: ada with Ada.Strings.Unbounded; package Pkg1 is X : constant Ada.Strings.Unbounded.Unbounded_String := Ada.Strings.Unbounded.To_Unbounded_String ("ABC"); ``use`` を使えばパッケージ名は省略できます。 それでも関数名と括弧は必要です。 .. code-block:: ada with Ada.Strings.Unbounded; package Pkg1 is use all type Ada.Strings.Unbounded.Unbounded_String; X : constant Ada.Strings.Unbounded.Unbounded_String := To_Unbounded_String ("ABC"); そこでよく使われていたテクニックが単項 ``"+"`` 演算子の悪用です。 .. code-block:: ada with Ada.Strings.Unbounded; package Pkg1 is function "+" (S : String) return Ada.Strings.Unbounded.Unbounded_String renames Ada.Strings.Unbounded.To_Unbounded_String; X : constant Ada.Strings.Unbounded.Unbounded_String := +"ABC"; 一口に悪用と言いましても単項 ``"+"`` 演算子は元々値をそのまま返す演算子です。 値はそのまま型だけ変える用途に使うのは某言語で ``<<``、``>>``、``/`` 等の演算子を意味を変えて使ってしまっているよりも随分まともと言えましょう。 Ada 202xでの改善 ---------------- Integer_Literalアスペクト/Real_Literalアスペクト/String_Literalアスペクト +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ``Integer_Literal``、``Real_Literal``、``String_Literal`` アスペクトが追加されました。 文字列からユーザー定義型への変換関数を指定します。 文脈に応じて対応するリテラルがその型として解釈されるようになります。 元々Adaのリテラルは特に修飾しなくても文脈に応じて型が変わっていました。 .. code-block:: ada declare U : constant := 0; -- universal integer X : Integer := 10; Y : Short_Integer := 20; Z : Interfaces.Unsigned_32 := 30; なおuniversal_integerは型が付く前のコンパイル時定数です。 ``U`` はオブジェクト(型付きの定数)ではなくてnamed numberと呼ばれる型なしの定数となり整数リテラル同様に使用できます。 明示的に修飾するならこうですね。 .. code-block:: ada declare U : constant := 0; -- universal_integer X : Integer := Integer'(10); Y : Short_Integer := Short_Integer'(20); Z : Interfaces.Unsigned_32 := Interfaces.Unsigned_32'(30); さて ``Integer_Literal`` を使ってみます。 .. code-block:: ada declare type Wrapped_Integer is record Element : Integer; end record with Integer_Literal => To_Wrapped_Integer; function To_Wrapped_Integer (X : String) return Wrapped_Integer is (Element => Integer'Value (X)); W : Wrapped_Integer := 40; やっていることは簡単ですね。 リテラルが文字列で渡されてくるので注意が必要です。 そうでなければ ``Long_Long_Integer`` や ``Long_Long_Float`` を超える数値型等を定義して使うことができなくなりますので妥当といえば妥当なのですが、変換関数の実装によっては非常に効率が悪いことになりかねません。 変換関数は是非ともstatic式化したいところです。 (``'Value`` 属性がstaticかどうかで実装のしやすさが大きく変わりますよね。 規格では明記されてなさそうです。 GNATで試したところstaticではありません。) ``Real_Literal`` と ``String_Literal`` も ``Integer_Literal`` と大体同じです。 ただし ``String_Literal`` の引数の型は ``Wide_Wide_String`` となっています。 またユーザー定義リテラルを使用したライブラリとして任意精度整数/実数が追加されました。 AIの番号としてはこちらが先ですのでこのライブラリのためにユーザー定義リテラルが追加されたと言ったほうが正しいかもしれません。 Ada.Numerics.Big_Numbers ++++++++++++++++++++++++ | http://www.ada-auth.org/standards/2xrm/html/RM-A-5-5.html Ada.Numerics.Big_Numbers.Big_Integers +++++++++++++++++++++++++++++++++++++ | http://www.ada-auth.org/standards/2xrm/html/RM-A-5-6.html Ada.Numerics.Big_Numbers.Big_Reals ++++++++++++++++++++++++++++++++++ | http://www.ada-auth.org/standards/2xrm/html/RM-A-5-7.html ``Big_Reals`` は浮動小数点数ではなくて分数になります。 GMPで言えば ``q`` です。 関連AI ------ - `AI12-0208-1`_ **Predefined Big numbers support** - `AI12-0249-1`_ **User-defined numeric literals** - `AI12-0295-1`_ **User-defined string literals** - `AI12-0325-1`_ **Various issues with user-defined literals** 所感 ---- まさかこんな機能が入るとは思ってもみませんでした。 暗黙の型変換よりはHaskellで ``fromInteger`` を実装するのに近い挙動ですのでいいのか……な。 構文糖ですけれども割と異物感は少ないです。 ただ数値リテラルの方はともかく、文字列リテラルは私としてはちょっと異議ありですね。 文字列は文字の配列ですので ``"ABC"`` は ``['A', 'B', 'C']`` としてgeneralized array aggregateで扱っていいのではと思ってしまいます。 .. _`AI12-0208-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0208-1.txt .. _`AI12-0249-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0249-1.txt .. _`AI12-0295-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0295-1.txt .. _`AI12-0325-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0325-1.txt