Ada 2020 (4日目) - target name symbol

昨日の使われてなかった記号を使うようになった話の流れで残る @ を先に片付けます。 適切な説明の順番? 脳内で並び替えてください。

@

って何に使われている記号でしょうね。

メールアドレスの@はそのままatの意味です。 Adaでは at は予約語ですのでこの用法では必要ないですね。

diffではhunkを表す記号として使われています。 行位置を囲っていることからatを意図しての用法と思われます。

twitter等では宛先としてtoの意味で使われています。

(拡張された)Pascalの@はアドレス演算子です。

P := @X;
P^ := P^ + 1 { X := X + 1 と同じ }

ただ、object at addressならともかくaddress at objectの形になりますので、逆ではないかという指摘があります。

Objective-Cの@はC言語に対する拡張ワードの頭に付く記号です。

例としては @end @implementation @interface @protocol @selector……他多数。

C#の@はraw stringです。

string s = @"\b\w+\b";

D言語、Objective Camlの@は属性です。

@nogc void foo();
@property int data();
external ( & ) : bool -> bool -> bool = "%sequand"
  [@@ocaml.deprecated "Use (&&) instead."]
external ( ** ) : float -> float -> float = "caml_power_float" "pow"
  [@@unboxed] [@@noalloc]

Javaのアノテーション、JavaScriptのデコレータも、機能としてはともかく記号の使われ方としては同じようなものです。

Perlでは@は配列全体を表します。

@array = (1, 2, 3)
print($array[0]) # 1

昔のMicrosoft Basicでは@はCURRENCY型です。 固定小数点型です。

C@ = 1234.5678

(32bit時代の?)Microsoftのstdcall呼び出し規約ではシンボルの後に引数のバイト数を続ける記号でした。

doxygen他いくつかのドキュメントジェネレーターでは特殊コマンドとして使われていることがあります。

いくつかのバージョン管理ソフトではリビジョンを表記するための記号として使われていることがあります。

makeの自動変数 $@ は現在のターゲット名です。

ローグ(ローグライクゲームの元祖ローグ)では@は自分を表します。

Ada 2020では@は

代入先への参照です。

declare
   X : Float := 0.0;
begin
   X := X + 1.0;

このXに1を加える代入文は

begin
   X := @ + 1.0;

と書き換えることができます。

記号の用途としてはmakeやローグが一番近いですね! これが言いたかっただけです!

これも [ ] と同じでpygmentsがエラーになりますね。

経緯

Ada 2012でユーザー定義indexingが追加されました。

declare
   package Float_Vectors is new Ada.Containers.Vectors (Positive, Float);
   L : Float_Vectors.Vector;
begin
   Float_Vectors.Append (L, 0.0);

といったVectorがあるとします。 これに対して

begin
   L (1) := L (1) + 1.0; -- error

と書いてしまうと、実はエラーでした。 この文は次のように展開されます。

begin
   Float_Vectors.Reference (L, 1).Element.all :=
     Float_Vectors.Constant_Reference (L, 1).Element.all + 1.0

Constant_Reference 関数は、C++で言うところの const_reference operator[](size_type n) const; です。 Reference 関数は、C++で言うところの const の付いていない方の reference operator[](size_type n); です。

これらの関数はこのように定義されています。

function Constant_Reference (Container : aliased in Vector;
                             Index     : in Index_Type)
  return Constant_Reference_Type;
function Reference (Container : aliased in out Vector;
                    Index     : in Index_Type)
  return Reference_Type;

Constant_Reference の引数は in ですが Reference の引数は in out となっています。 Reference では返した参照を使って中身が変更されることに備えた追加の処理が必要になる場合があるためです。 例えば実データを参照カウンタで共有している場合にはコピーして共有を解除する必要がある等です。

そして、ひとつの文中で in out パラメータに渡したオブジェクトを同時に他に使うことはできません。 評価順によって動作が変わってしまうからです。 この例ですと Constant_Reference が先に呼ばれてしまいますと、変更前のデータに対する参照が返されます。 その後 Reference によってデータが変更されてしまいますと、Constant_Reference が返した参照にアクセスすることは何らかの違反となります。 そのためこのような呼び出しはエラーです。

これは、仮に右辺も Reference の呼び出しで、Reference を2回呼ぶことにしても同じです。 Ada 2012時点の解決方法としては、参照を束縛して使い回せばよいです。

declare
   Ref : Float_Vectors.Reference_Type renames L (1);
begin
   Ref := Ref + 1.0;

<読み飛ばし推奨> C++ではこれらのユーザー定義演算子は現在位置からのオブジェクトへのアクセス権が const かどうかで呼び分けられますが、Adaでは右辺か左辺かで呼び分けられます。 const の付いていない方は追加の処理があるかもしれませんので、できれば const の付いている方を呼ぶほうが良いです。 この点Adaの方がC++より良さそうですが、ただAdaでは constant の有無でオーバーロードができるのはこのユーザー定義indexingのみとなっています。 一般のサブプログラムではこのようなオーバーロードはできません。 C++はできます。 </読み飛ばし推奨>

Ada 2020での改善

@ を使うことで、わざわざ追加の宣言を書く必要がなくなります。

begin
   L (1) := @ + 1.0

関連AI

  • AI12-0125-3 Add @ as an abbreviation for the LHS of an assignment

  • AI12-0322-1 Equivalence for the target name symbol

ボツ案の紹介

X'Inc;
X'Inc (1);
X :+ 1;

単にインクリメントしたいだけならこれらの案の方が短く書けますが、ユーザー定義indexingを考えますとあらゆる用途をカバーする必要がありますので @ に軍配が上がりました。 特定の演算子以外のケースにボツ案では対応できません。

begin
   L (1) := Sin(@);

また @ は複数回用いることもできます。

declare
   X : Ada.Numerics.Complex_Types.Complex := (Re => 2.0, Im => 3.0);
begin
   X := (Re => @.Im, Im => @.Re); -- 実数部と虚数部の交換

@ の代わりの記号としても様々な提案がなされましたが、ベニントンで行われたミーティングの結果 @ に決まったようです。 メーリングリストによる議事録はこのように記録されているわけですが、オフラインミーティングの議事録は公開されていないようで悲しいです。 ミーティングの議事録はAIとは別に公開されています。

所感

風来のシレンはもう新作出ないのでしょうか。 出ても、世界樹と不思議のダンジョンの出来を見る限り期待ができそうにないのが悲しいです。