型修飾 (調べ物パート)

FLTVの発表資料のひとつ、luciferの設計コンセプトに触発されて、妄想を垂れ流したいと思いました。

ここでの型修飾という言葉は言語組み込みの機能を指します。Cのconstみたいなやつです。C++のauto_ptrやC#のNullableやJavaの属性みたいなユーザー定義可能なものではないです。例えばD言語のimmutableをユーザー定義可能にする労力を想像していただければ。

まずは既存のものを調べます。*1

lucifer

  • inaccessible (opequa typeっぽい)
  • readable
  • writable
  • variable (readableかつwritable)
  • immutable
  • anonymous (右辺値)

C

  • const
  • restrict
  • volatile
  • __attribute(())__はキリがないので省略

D

  • const
  • immutable
  • 推移的

Ada

  • aliased (ポインタ経由で参照される)
  • constant
  • not null (null代入禁止ポインタ)
  • range (数値の範囲制限、あと配列にも)
  • 'Storage_Pool (newしたときのメモリマネージャを指定)
  • 'Storage_Size (0にするとnewを禁止できる)
  • pragma Atomic (x86のlockプレフィクスのようなもの)
  • pragma Finalize_Storage_Only (終了時はデストラクタをサボる、GNAT拡張)
  • pragma No_Strict_Aliasing (Cのrestrictの逆、GNAT拡張)
  • pragma Preelaborable_Initialization (静的に初期化可能)
  • pragma Volatile (Cのvolatile)
  • メモリレイアウト関係は多すぎるので省略
  • 型としての振る舞いに関係ないものも省略
  • あとdiscriminant*2もユーザー定義constraintとして使える
  • 絶対漏れあります
  • Cの__attribute(())__は省略したのにこっちにはGNAT拡張を書く辺り卑怯です

*1:http://www.kmonos.net/wlog/100.html#_1705090831の『なにか Ada や〜』への補足にもなれば幸いです。Eiffelについてはどなたかお願いします。

*2:O'Camlのtype t = T of intのintみたいなの。int部分が1限定のsubtype、2限定のsubtype……が作れます。

クロージャで簡易ストリーム

もうイテレータはどうでもよくてAda.Containersをdisっているだけです。
O'Camlでよく見るスタイルを真似るのはどうでしょう。

let print_all (print : 'a -> unit) (next : unit -> 'a option): unit = (
   match next () with
   | Some item ->
     print item;
     print_all print next
   | None -> ()
);;

(* let input_from_stream s = let r = Stream.peek s; Stream.junk s; r *)
print_all print_int (input_from_stream (Stream.of_list container))
generic
   type Element (<>) is limited private;
   with Put (Item : Element) is <>;
procedure Generic_Put_All (Next : function return access constant Element) is
   Item : access constant Element := Next.all;
begin
   if Item <> null then
      Put (Item.all);
      Put_All (Next);
   end if;
end Put_All;

declare
   Index : Cursor := First(Container);
   function Next is new Containers.Generic_Next (Container, Index); -- 実装略
   function Put_All is new Generic_Put_All (Integer);
begin
   Put_All (Next'Access);
end;

input_from_streamだのGeneric_Nextだのはあらかじめライブラリ側で用意しておくとして、Adaで書いてもそんなに悪くない気がしませんか。

イテレータ

Iterator に抵抗があるなら、自分の時代遅れを見直した方が良いです。
http://twitter.com/shinji_kono/status/3317315646

……と言われたので、イテレータについて調べてみました。

プログラミング言語において配列やそれに類似するデータ構造の各要素に対する繰返し処理の抽象化である。
http://ja.wikipedia.org/wiki/%e3%82%a4%e3%83%86%e3%83%ac%e3%83%bc%e3%82%bf

一般には、Rubyのやつと、C++のやつが幅を効かせている様ですが、結構それ以外もあります。節操の無い言語は複数種類用意してますね。
それで、節操の無いAdaは、更にもう1種類追加しようとしているわけです。

http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai05s/ai05-0139-1.txt

これには、今あるループ方法が果てしなく面倒くさいという理由があります。

外部イテレータの例

for(container_t<element_t>::iterator position = container.begin(); position != container.end(); ++position){
  ... --中身は*position
}
declare
   Position : Cursor := Container.First;
begin
   while Has_Element(Container) loop
      ... --中身はElement(Position)
      Next(Container);
   end loop;
end;

内部イテレータの例

Container.iter (fun item -> ...) container
declare
   procedure Process (Position : Cursor) is
   begin
      ... -- 中身はElement(Position)
   end;
begin
   Iterate(Container, Process'Access);
end;

やってられるかー!
というわけで列挙オブジェクトタイプが追加。このタイプは、文法の支援があるのも特徴ですね。

foreach (Element item in container)
{
   ...
}
for Position in Container.Iterator loop
   ... -- 中身はElement(Position)
end loop;

いきなりすっきりしました。

……いや、それはいいとして。

<ここからごみ>ここから私の主張。

C++がそうしたからでしょうけれど、外部イテレータの多くが、vectorやarray等要素がメモリ上で連続することが保証されているコンテナですら、配列のインデックスとは別物なのは、少し悲しいと思うのです。なぜvector::iteratorがssize_tではいけなかったのか?これでは、統一と称して別ライブラリ作ったら結局選択肢が増えただけでうざーなよくある現象にも思えます。
vectoriteratorは、コンテナへの参照と、インデックス値を持っていますので、レジスタにも載らなかったりして、色々と勿体ないわけですよ(と思ったがそんなヘボ実装はAda.Containers.Vectorsぐらいか?普通element_t *にするよなあ……これだからGNATは……。しかし私の使用頻度としてはAda.Containers.Vectorsのほうがstd::vectorより多いためry)。
それ以前にインデックスとイテレータの2種類があるのが嫌。
イテレータからの値の取得をoperator *ではなく、operator []で行うことで、vector::iteratorをssize_tと同一にできて、幸せになれるはずです。主にGNATで!あとポインタ演算ができない言語や範囲チェックを実行時に行いたい場合(GNATでコンテナとインデックスの組なのはどう考えてもこれが理由だろ俺)で外部イテレータを実装するとき!
C++の場合は、C言語にポインタでループしてたイディオムがありましたので、element_t *がイテレータなのは自然な流れですが、それまでインデックスアクセスしてた言語がいきなりSTL風に染まってしまうのは、なんかこう、なんかこう。お茶は続いてほしかった。なんかこう。D言語のforeachなんか、やはり不便とわかったのか後からインデックスも受け取れるようになりましたし!
逆に、リンク系コンテナで、無駄にコンテナを渡すことになりますが、使っていない引数の除去は、構造体の分離より簡単なはず……。

</ここまでごみ>

もうひとつAda.Containersには問題がありまして(もうイテレータと関係ないですが)、上でも書いている要素取り出しのためのElement関数は関数のため、型によっては毎回返値をコピーして悲惨なことになるわけです。
申し訳程度にQuery_Elementみたいなものも用意されていますが、誰が要素アクセスのために毎回こんなもの書くというのか……。

declare
   procedure Process (Item : Element_Type) is
   begin
      ... --Itemはコピーされない!
   end Process;
begin
   Query_Element (Position, Process'Access);
end;

当然のように、access型を返す関数も用意するように提案がありました。

http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai05s/ai05-0142-4.txt

function Constant_Reference (Container : aliased in Vector; Position : in Cursor)
   return Constant_Reference_Type;
function Reference (Container : aliased in out Vector; Position : in Cursor)
   return Reference_Type;
Nanka_Op (Reference(Container, Position).Element.all); -- コピーされない!

こうなるとElement関数やQuery_ElementやUpdate_Elementはお払い箱になるわけですが、Adaのことですから規格改訂時に互換性を無視してばっさりと削ぎ落としてくれることでしょう。

ここで注目して欲しいのはReference関数のシグネチャ。Element関数を定義している以上はCursorだけで要素アクセスに充分な情報があるはずなのに、Containerが引数にあります。

……いやaliased引数も大きいですよ?(でもAda.Containers.VectorsのGNAT実装の時点で、引数に'Unchecked_Accessを使ってしまっているしなあ。参照渡しとaccessが内部的に異なる環境ってあるのでしょうか)

あれ、俺いつのまにAda Issuesに妄想を投稿したっけ……

http://www.ada-auth.org/AI05-SUMMARY.HTML
Ada IssuesにあまりにもAdaらしくない提案が連投されて、Randy氏が投げた……。

Following is one of a number of "trial-balloon" proposals for future enhancements to Ada. I've been thinking about these more than my real work lately, so by writing them up I can stop thinking about them. (And written up, problems should be more evident than they would be in a simple question format.) Note that I haven't discussed these with anyone else yet, so they are pretty much solely from my fertile brain: don't expect to be using these features next year... :-)
http://www.ada-auth.org/cgi-bin/cvsweb.cgi/ai05s/ai05-0141-1.txt

てきとー超訳

Adaの拡張の試案を連投してるけどさ、そのたびに俺はいちいち考えないといけないわけで俺の仕事を遅らせたいのか。読むのめんどいし。(問題を単純かつ詳しく清書してくれ)。ああそれから俺はまだ誰とも話を通しては無いけどさ、私の灰色の脳細胞が示唆するに、これらの提案は没になるからそのつもりで……。

しかも提案の内容が無闇やたらと案を書き連ねているだけの上に、あまりにも私の夢想していたことと重なっててこれは笑うしかない。誰でも考えるのですね。
そりゃ.allのオーバーロードとかスコープに縛られないポインタとかUnchecked_Deallocation長いからFreeと書きたいとかラムダ式の構文糖衣とかAda使ってたら欲しくて欲しくてたまらなくなりますけどさ、そーゆーのは規格に入れちゃダメだろう……。独自拡張レベルでは存在していていいと思うしあったら便利に使いまくると思いますけれども。
あーでもVariable function resultsは(提案された文法は当然却下として)何らかの形で入れて欲しいなあ……。

そしてそのRandy氏もすぐ横ではUser-defined iteratorsのようなものを提案してたりして(当然練られている度合いは大幅に違うのですが)、やっぱりAda委員会は面白すぎるwwwヲチスレ欲しいwww

とりあえず登場人物紹介みたいなのはあると嬉しいですがどこかにないでしょうか……。
そういえば石川さんはAdaの偉い人の肩書きをきちんと把握していて凄かったのでした。

Ada Hackathon補足

ytqwerty2009-02-16

Agdaいいですね。(枕詞)
さて、当日までひとりHackathonを覚悟していたのですが、最終的に5人になりまして、皆様ありがとうございました。
石川さんはNACLの遥か先のステージへ。稲葉さんやh_sakuraiさんはきちんとAda「を」作っていて凄いと思いました。shinichiro.hさんは、Ada86の本のみでスレッドプールの実装……!私自身の作業は……NACLのビルドに即効挫折してgdgd。無線通信が使えないのも私ひとりという悲しい事態でした。後半は前々から作ってた.hのトランスレータ(使用言語はO'Caml)の作業をしてました。まだ見せられるレベルでは無いです……。
以下後付け補足です。

続きを読む

問題です。

次のプログラムはなんと出力するでしょう。

with ada.text_io;
with ada.integer_text_io;
procedure a is
   k : boolean := true;
   type t is record x : integer; end record;
   function z return t is
      x : integer := 10;
   begin
      if k then
         k := false;
         ada.integer_text_io.put(z.x); ada.text_io.new_line;
      end if;
      return (x => 20);
   end z;
begin
   ada.integer_text_io.put(z.x); ada.text_io.new_line;
end a;

他の言語だとネタにしようと思うのにAdaだと曖昧な文法だよorzとしか思わない謎。