それAdaならできるよ

それ、Adaならできるよ

Adaなら83の時代から部分集合余裕だぜー。ていうか、JavaってなんでJ2SE5まで列挙型なかったんだろね。気持ちは何となく分かるけども。

Ascii_StringString型がまんまだし、Non_Negative_IntegerもやっぱりPositive型が最初からIntegersubtypeとして定義されているのでそれを使えばよし。

Scoreはこんな感じかな。派生型を使ってIntegerとの演算を禁止してるけど、状況によってはやり過ぎかも。どうせならScore_nも派生型にして、100と1000の間での演算を禁止してもいいかな。めんどくさい場合はIntegersubtypeにしちゃえば、型変換が不要なので楽ちん。

type Score is new Integer range 1 .. Integer'Last;
subtype Score_100 is Score range Score'First .. 100;
subtype Score_1000 is Score range Score'First .. 1000;

で、ふと気がついたんだけど、

type Non_Negative_Integer renames Positive;

って認められないのね。普通使わないからいいけど、packageをrenamesできるんだから、typeでもできるといいな。


次、UserIDの話。

Ada95は勉強中だから若干自信ないけど、インスタンス変数の実際のところであるtagged recordはどうやらrecordと同じく要素の内容を実行時に変化できるらしいので、こんな感じで行ける。Javaでいうとコンストラクタ次第でインスタンス変数が変化する感じ? なのかな。

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;

procedure Sample is

   package User_ID is
      type ID_Type is (Number, Handle);
      type Object(T : ID_Type) is tagged record
         case T is
            when Number =>
               Number : Integer;
            when Handle =>
               Handle : Unbounded_String;
         end case;
      end record;
   end User_ID;

   -- User_IDのサブクラスなら何でも入るポインタ
   type User_ID_Ptr is access User_ID.Object'Class;

   package User_Handle is
      -- 制約はここで注入
      type Object is new User_ID.Object(T => User_ID.Handle) with null record;
   end User_Handle;

   package User_Number is
      -- 制約はここで注入
      type Object is new User_ID.Object(T => User_ID.Number) with null record;
   end User_Number;

   X : User_ID_Ptr;
   Y : User_ID_Ptr;

begin
   -- User_IDを使う場合(その場でHandleかNumberかを決められる)
   X := new User_ID.Object(T => User_ID.Number);
   X.Number := 1000;
   Y := new User_ID.Object(T => User_ID.Handle);
   Y.Handle := To_Unbounded_String("Foo");

   Put(X.Number); New_Line;
   Put(To_String(Y.Handle)); New_Line;
   --  1000
   -- Foo


   -- User_HandleとUser_Numberを使う場合
   X := new User_Handle.Object;
   X.Handle := To_Unbounded_String("Bar");
   Y := new User_Number.Object;
   Y.Number := 2000;

   Put(To_String(X.Handle)); New_Line;
   Put(Y.Number); New_Line;
   -- Bar
   --  2000
end Sample;

恐ろしくすっきりと書けますな。やはりAdaは最強ですね!