Ada 202x (41日目) - その他のrecordの変更 ======================================== .. post:: Jan 10, 2020 :tags: ada, ada_2022 .. |ZWSP| unicode:: U+200B .. ZERO WIDTH SPACE :trim: あまりにも細かいやつは紹介しなくていいとは思うのですが、実際書き始めると自分でも案外発見があったりしますので一応見ていきます。 バリアントレコード ------------------ 今までaggregate式でバリアントレコードの可変部分を書くときはdiscriminantはstaticである必要がありました。 .. code-block:: ada type D_Type is (A, B, C); type VR (D : D_Type) is record case D is when A | B => Element_1: Float; when C => Element_2 : Character; end case; end record; function Create (D : D_Type) return VR is (case D is when A => VR'(D => A, Element_1 => 1.0), when B => VR'(D => B, Element_1 => 1.0), when C => VR'(D => C, Element_2 => 'C')); discriminantを実行時の値で指定するならこんな風にする必要がありました。 .. code-block:: ada function Create (D : D_Type) return VR is begin return Result : VR (D => D) do case D is when A | B => Result.Element_1 := 1.0; when C => Result.Element_2 := 'C'; end case; end return; end Create; つまり ``A`` の場合と ``B`` の場合をまとめようとすればひとつの式で書けませんでした。 ひとつのaggregate式で済まないということは要素の指定漏れがあってもチェックされなくなるということでもあります。 Ada 202xでは可変部分を書くときもdiscriminantを実行時の値で指定できるようになりました。 discriminantが合わない場合は ``Constraint_Error`` になります。 .. code-block:: ada function Create (D : D_Type) return VR is (case D is when A | B => VR'(D => D, Element_1 => 1.0), when C => VR'(D => C, Element_2 => 'C')); Unchecked_Unionとaggregate式 ---------------------------- 今まで ``Unchecked_Union`` のaggregate式にnamed associationを使えませんでした。 .. code-block:: ada type U32 (D : Integer := 0) is record case D is when 0 => As_Float : Float; when others => As_Unsigned : Interfaces.Unsigned_32; end case; end record with Unchecked_Union; X : U32 := (0, 1.0); Y : U32 := (D => 1, As_Unsigned => 16#3f80_0000#); -- error Ada 202xからは使えるようになりました。 .. container:: gray-and-small pygmentsさん16進リテラルに ``_`` 挟んだだけでエラーになるの情けないでしょう……。 Unchecked_Unionとin式 --------------------- ``Unchecked_Union`` の比較では両辺がconstrained、つまり型でdiscriminantが固定されていなければなりません。 いずれかがunconstrainedの場合は ``Program_Error`` になります。 .. code-block:: ada declare subtype Float_U32 is U32 (0); begin if Float_U32 (X) = Float_U32 (Y) then null; end if; でないと ``Float`` と ``Interfaces.Unsigned_32`` のどちらで比較していいかわからなくなるからですね。 in式では値と型の関係のため基本的に全部 ``True`` になりますが、unconstrained ``in`` constrainedの場合だけ ``Program_Error`` になります。 .. code-block:: ada begin if Float_U32 (X) in U32 then null; end if; if Float_U32 (X) in Float_U32 then null; end if; if X in U32 then null; end if; if X in Float_U32 then -- Program_Error null; end if; ``X in Float_U32`` を求めるには ``X.D`` を確定させる必要があるからでしょうか。 Ada 2005のときのAIを見てみましたが理由は簡単には読み取れそうにないです。 - `AI95-00216-01`_ **Unchecked unions -- variant records with no run-time discriminant** で、まあ、ここまで前置きです。 Ada 2012でin式はcase文のように複数の選択肢を続けて書けるようになりました。 .. code-block:: ada begin if X in U32 | Float_U32 then null; end if; この場合左から ``X in U32`` が先に判定されて ``True`` になるのか、それとも ``Program_Error`` になるのか曖昧でした。 Ada 202xで ``Program_Error`` になると確定しました。 抽象型とin式 ------------ ユーザー定義の ``"="`` 演算子を持つ抽象型で実体が基本型の場合、実体が可視の箇所からはin式が禁止されます。 .. code-block:: ada package Pkg1 is type T is private; function "=" (Left, Right : T) return Boolean; function Is_Zero (X : in T) return Boolean; private type T is range -100 .. 100; end Pkg1; package body Pkg1 is function "=" (Left, Right : T) return Boolean is begin return Integer (Left) = Integer (Right); end "="; function Is_Zero (X : in T) return Boolean; begin return X in 0; -- error end Is_Zero; end Pkg1; 抽象型のin式は ``"="`` が使われます。 対して基本型のin式はユーザー定義の演算子を介さずに直接比較されます。 両方の性質が可視の箇所ではエラーになるようになったというわけです。 ``Pkg1`` の外で ``Pkg1.T`` にin式を使うことはでき ``"="`` が使用されます。 ドット記法のrenames ------------------- ``renames`` でドット記法に別名を与えるときはオブジェクト名部分も ``renames`` 可能でなければならないルールが追加されました。 .. code-block:: ada type T is tagged null record; procedure Method (Object : in T) is null; Object : T; procedure Bound renames Object.Method; この ``Object`` の部分が ``renames`` できないと駄目、と言いましてもドット記法が書けるのはtagged型のみですから駄目なケースも限られます。 具体的にはバリアントレコードの可変部分では駄目ということです。 .. code-block:: ada type V_Type (D : Boolean := False) is record case D is when False => Element : T; when True => null; end case; end record; V : V_Type := (D => False, others => <>); procedure Bound renames V.Element.Method; -- error ユーザー定義indexingもこれに倣います。 上書き不可能なアスペクト ------------------------ tagged型に付くアスペクトには一度指定すると派生先で上書き不可能(nonoverridable)なものがあります。 - ``Aggregate`` - ``Constant_Indexing`` - ``Default_Iterator`` - ``Implicit_Dereference`` - ``Integer_Literal`` - ``Iterator_Element`` - ``No_Controlled_Parts`` - ``Real_Literal`` - ``String_Literal`` - ``Variable_Indexing`` .. container:: inserted ``Integer_Literal`` 、 ``Real_Literal`` 、 ``String_Literal`` については `AI12-0342-1`_ で一度上書き可能になって `AI12-0419-1`_ で再び上書き不可能になりました。 (ざっと見ただけですのでまだあるかも知れません。) ``No_Controlled_Parts`` 以外は全部コンテナ用の構文糖ですね。 つまりはアップキャストやダウンキャストで構文糖の展開を変えることはできません。 この上書き不可能なアスペクトにいくつかルールが追加されました。 Constant_Indexing |ZWSP| /Variable_Indexing +++++++++++++++++++++++++++++++++++++++++++ ``Constant_Indexing`` と ``Variable_Indexing`` は一度に指定しないといけなくなりました。 基底型で ``Variable_Indexing`` だけ指定しておいて派生先で ``Constant_Indexing`` を追加するようなことは禁止されました。 Objective-Cのように ``NSMutableArray`` を ``NSArray`` から派生させるようなことはやめた方がいいですね。 .. container:: gray-and-small <読み飛ばし推奨> ちなみに変更可能不可能を継承階層で表現するなら、D言語のconstとimmutableに習って読み取り専用の抽象型から変更不可能と変更可能の両方を派生させるのが賢いやり方と思います。 他の参照を通じて変更されるかもしれないのと、本当に変更不可能なのとを区別できますので。 interface +++++++++ ``interface`` から継承した上書き不可能なアスペクトが衝突した場合エラーになると定められました。 .. code-block:: ada type I1 is interface with Constant_Indexing => Constant_Reference; type Reference_Type (Element : not null access Float) is null record with Implicit_Dereference => Element; function Constant_Reference (Object : I1; Index : Positive) return Reference_Type is abstract; type I2 is interface with Constant_Indexing => Element; function Element (Object : I1; Index : Positive) return Float is abstract; type T is new I1 and I2 with null record; -- error この例では ``Constant_Indexing`` が衝突しています。 Adaの ``interface`` はEiffelやDelphiやC#のようにサブプログラム名の衝突を解決することもできませんし、こうして組み合わせが確実にエラーになる例もできてしまいましたしで使い辛いです。 やはり継承よりも ``generic`` 以下略。 関連AI ------ - `AI12-0086-1`_ **Aggregates and variant parts** - `AI12-0160-1`_ **Adding an indexing aspect to an indexable container type** - `AI12-0162-1`_ **Memberships and Unchecked_Unions** - `AI12-0174-1`_ **Aggregates of Unchecked_Unions using named notation** - `AI12-0204-1`_ **Renaming of a prefixed view** - `AI12-0211-1`_ **Interface types and inherited nonoverridable aspects** - `AI12-0328-1`_ **Meaning of limited type and record type in 4.5.2(28.1/4)** .. _`AI95-00216-01`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ais/ai-00216.txt .. _`AI12-0086-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0086-1.txt .. _`AI12-0160-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0160-1.txt .. _`AI12-0162-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0162-1.txt .. _`AI12-0174-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0174-1.txt .. _`AI12-0204-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0204-1.txt .. _`AI12-0211-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0211-1.txt .. _`AI12-0328-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0328-1.txt .. _`AI12-0342-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0342-1.txt .. _`AI12-0419-1`: http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai12s/ai12-0419-1.txt