Adaの日本語情報を増やす(4)

整数型とIntegerの派生型(Derived Type)の違いと部分型(subtype)の違い。微妙に分かりにくいので解説しながら整理してみる。

まず、部分型はその通り、親の型の1部だけを抜き取って型として定義する場合に使います。IntegerPositiveですね。部分型の場合、同じグループに属していると見なされるので、IntegerPositiveを代入することができます。部分型の制約に違反しないかがいり、あまり型については気にしないでいいでしょう。制約は緩めと言えます。

つぎに整数型ですが、これは「整数を値に持つ何か」です。「何か」であって「整数」それ自体ではないと思うのがいいかもしれません。たとえば1月から12月までを

type Month is range 1 .. 12;

などと定義することがよくあります。単純なInteger型とは厳密に区別されますし、

type Day is range 1 .. 31;

別に定義された他の整数型とも区別されます。具体的にいえば異なる整数型間での演算はできません。もっとも、「月」と「日付」を足し合わせる、などということはあり得ないことなので、もしそのようなコードがあればそれはおそらくバグでしょう。数値型が厳密に区別されるおかげで、コンパイルの段階でそういったバグを検出することができます。どうしても代入などを行いたい場合は明示的な型変換を行う必要があります。

最後に派生型ですが、整数型と同様、派生元の型と派生されたは厳密に区別されます。さらに、派生型の特徴の1つに、派生元の型が定義されているパッケージにおいて、その型を引数もしくは返値に持つ全てのサブプログラム(procedurefunction)が定義されていれば、派生した型に同じサブプログラムが継承される、という点があります。この性質はとても強力で、様々な処理を持つ型を違う目的で使う際にとても役に立ちます。逆にIntegerが定義されているStandardパッケージには、Integerに関するサブプログラムがないため、実際のところIntegerからの派生は余り意味がありません。

というわけで、分かりにくいサンプルプログラムです。

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure Sample is

   -- Standard.Integer にはサブプログラムがないので、 改めて Integerを作る。
   package Nise is
      type Nise_Integer is range Integer'First .. Integer'Last;
      procedure Procedure_For_Nise_Intger (X : Nise_Integer);
   end Nise;

   package body Nise is
      procedure Procedure_For_Nise_Intger (X : Nise_Integer) is
      begin
         Ada.Integer_Text_IO.Put(Integer(X)); New_Line;
      end Procedure_For_Nise_Intger;
   end Nise;
   use Nise;


   -- 部分型による正の数の表現
   subtype S_Positive is Nise_Integer range 1 .. Nise_Integer'Last;

   -- 数値型による正の数の表現
   type I_Positive is range 1 .. Nise_Integer'Last;

   -- 派生型による正の数の表現
   type D_Positive is new Nise_Integer range 1 .. Nise_Integer'Last;

   Int_X : Nise_Integer;
   S_Pos : S_Positive;
   I_Pos : I_Positive;
   D_Pos : D_Positive;


begin
   Int_X := 10;
   S_Pos := 20;
   I_Pos := 30;
   D_Pos := 40;

   S_Pos := Int_X; -- 部分型なのでOK
-- I_Pos := Int_X; -- 区別されるので NG
   I_Pos := I_Positive(Int_X); -- 型変換しているので OK
-- D_Pos := Int_X; -- 区別されるので NG
   D_Pos := D_Positive(Int_X); --型変換しているので OK


   Procedure_For_Nise_Intger(S_Pos); -- 部分型なのでOK
-- Procedure_For_Intger(I_Pos);      -- 区別されるので NG
   Procedure_For_Nise_Intger(D_Pos); -- 派生型ではサブプログラムが継承されるのでOK

end Sample;