Ada 2020 (9日目) - declare expression

日が変わる前に間に合ったからセーフ。

経緯

Ada 2012で、式としての ifcase (conditional expression)、quantified式が使えるようになりました。

if式。折り返しを微妙に迷います。

(if A = B then X
 elsif C = D then Y
 else Z)

case式。

(case A is
   when B      => X,
   when C      => Y,
   when others => Z)

quantified式。 Boolean 限定のfold式みたいなものです。 for all はループ全てが条件を満たせば、 for some はループ中一回でも条件を満たせば True になります。

(for all I in 1 .. 10 => F (I))
  -- F (1) and then F (2) and then ... F (10)
(for some I in 1 .. 10 => F (I))
  -- F (1) or else F (2) or else ... F (10)

Haskellの Data.Foldable.allData.Foldable.any と思えばいいです。 (伝わらない説明。)

これらは、同じくAda 2012で導入された事前条件/事後条件等の条件式に複雑な式を書くために導入されました。

またAda 2012の改訂版であるTechnical Corrigendum 1で raise も式中で使えるようになり、例外が投げられるようになりました。

そしてAda 2020ではaggregate式で内包表記っぽいこともできるようになっています。

そう、手続き型言語Adaは既に式指向の言語として色々書けてしまうのです。 流行にかぶれてしまってますよねえ。

となると後足りないものは何でしょうか。

Ada 2020でのdeclare expression

式中での宣言もできるようになりました。 ……ヘンな文法で。

(declare
   X : constant T := V;
 begin
   F (X))
type Float_Array is array (Positive range <>) of Float;
Not_Found : exception;
function Binary_Search (C : Float_Array; Item : Float) return Positive is
  (if C'Length > 0 then
     (declare
        M : constant Positive := (C'First + C'Last) / 2;
      begin
        (if C (M) > Item then Binary_Search (C (C'First .. M - 1), Item)
         elsif C (M) < Item then Binary_Search (C (M + 1 .. C'Last), Item)
         else M))
   else
     raise Not_Found);

珍しい begin 単独での使用は珍しく珍しいですね。

declare式で宣言できるものは変数や定数と renames による別名に制限されていますので、型や関数内関数等は宣言できません。 関数内関数が使えれば無法地帯でしたのに残念。

Ada 2020でのrenamesの改善

式中に書くには constant 型名 というのは長い、と思われたようです。 色々と提案された結果最終的に renames で型を省略できるようになりました。

function Binary_Search (C : Float_Array; Item : Float) return Positive is
  (if C'Length > 0 then
     (declare
        M renames (C'First + C'Last) / 2;
      begin
        (if C (M) > Item then Binary_Search (C (C'First .. M - 1), Item)
         elsif C (M) < Item then Binary_Search (C (M + 1 .. C'Last), Item)
         else M))
   else
     raise Not_Found);

え、式を renames するのはありですかって? それは式によります。 この場合は (First + Last) / 2 の一番外側は / 演算子です。 そして / 演算子は "/" 関数の呼び出しです。 関数呼び出しの結果は一時オブジェクトですので renames も可能というわけです。

型名の省略は普通に文指向で書いているときにも有用です。 というわけで見慣れた形に書き直したものがこちら。

function Binary_Search (C : Float_Array; Item : Float) return Positive is
   First : Positive := C'First;
   Last : Natural := C'Last;
begin
   while First <= Last loop
      declare
         M renames (First + Last) / 2;
      begin
         if C (M) > Item then
            Last := M - 1;
         elsif C (M) < Item then
            First := M + 1;
         else
            return M;
         end if;
      end;
   end loop;
   raise Not_Found;
end Binary_Search;

ちなみに古代言語Modula-2ですら変数宣言時の型名の省略は可能でした。 今回採用されたのは renames のみと限定された形ですが、Adaでもようやくですね。

<読み飛ばし推奨> 個人的にはこれを型推論と呼ぶ風潮には反対します。 右辺をコンパイルする過程で型は特定できていますから型名を書いた場合と比べても何も推論してはいません。 </読み飛ばし推奨>

関連AI

所感

足りないのは末尾再帰最適化ですって? バックエンドに言ってください。