Ada 202x (21日目) - user-defined literal¶
息継ぎがしたいので寄り道回入れていいですか。 と言いましても割と大きな変更になります。
経緯¶
これまで標準の型をラップするライブラリを使うのが結構大変でした。 Adaでは暗黙の型変換は匿名のaccess型まわりとユーザー定義参照型ぐらいしかありません。 定数ひとつ作るにも関数呼び出しが必要です。
with Ada.Strings.Unbounded;
package Pkg1 is
X : constant Ada.Strings.Unbounded.Unbounded_String :=
Ada.Strings.Unbounded.To_Unbounded_String ("ABC");
use
を使えばパッケージ名は省略できます。
それでも関数名と括弧は必要です。
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");
そこでよく使われていたテクニックが単項 "+"
演算子の悪用です。
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のリテラルは特に修飾しなくても文脈に応じて型が変わっていました。
declare
U : constant := 0; -- universal integer
X : Integer := 10;
Y : Short_Integer := 20;
Z : Interfaces.Unsigned_32 := 30;
なおuniversal_integerは型が付く前のコンパイル時定数です。
U
はオブジェクト(型付きの定数)ではなくてnamed numberと呼ばれる型なしの定数となり整数リテラル同様に使用できます。
明示的に修飾するならこうですね。
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
を使ってみます。
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¶
Ada.Numerics.Big_Numbers.Big_Integers¶
Ada.Numerics.Big_Numbers.Big_Reals¶
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で扱っていいのではと思ってしまいます。