Commits

Anonymous committed 0d4e177

update

Comments (0)

Files changed (1)

+OCaml 4.02 の予習
+=================================
+
 printf が GADT で強化された
-=================================
+-----------------------------
 
 まあ簡単に言うと毎回プリントする再にインタープリットしていた format 文字列を
 もすこし代数的なデータに落して嬉しいみたいな感じ、なのだが、
 興味があったら Pervasives.CamlinternalFormatBasics と CamlinternalFormat を読めということらしい。
 
 しかし、こんな変更ガガっと入れるのはええけど、ちゃんとテストしてあるんか?
+これは不安ですね…
 
 module alias が軽くなった
-=================================
+---------------------------
 
 中の人がぼんやりしてたら、外の人がモジュールをごにょごにょ操作するしまくる
 OCaml プログラミングスタイルというのを確立してしまった感がある。特に Jane Street 
 
 
 string は immutable の方向へ
-===================================
+--------------------------------
 
 string の他に、 bytes という型が入った。通常は string と bytes は同じ型で単一化できる:
 
 例によって string はただの char 配列でしかないので UTF-8 とかを扱いたい時はライブラリを
 通す必要がある。
 
-- Attributes and extension nodes
-- Generative functors
-- Module aliases
-- Exception cases in pattern matching (patch by Jeremy Yallop).
-- Extensible open datatypes (patch by Leo White).
+実装上は実に簡単なのでバグとかは無いと思う。
 
 新しい文字列リテラル
-============================
+----------------------
 
 `{id|文字列|id}` という新しい文字列が書けるようになった。例えば `{|abcd"\/|}` と書くと、
 `"abcd\"\\/"` という文字列と見做される。 `{|..|}` の中では `\` は特殊文字と見做されないので
 冗長でちょっとウザと思うのだが、文字列の中に `|}` を書きたい場合はこれを使うのだと思われる。
 後多分 id は attribute として使えるはず。
 
+これも簡単な部類。ガンガン使っていけるはず。
+
+Generative functors
+--------------------------
+
+今までの OCaml の functor は applicative functor と呼ばれる:
+
+    module Make(A : sig end) = struct
+      type t = Foo
+      let x = Foo      
+    end
+
+    module Empty = struct end
+
+    module A = Make(Empty)
+    module B = Make(Empty)
+
+    let () = assert (A.x = B.x)
+
+が通る。つまり A.t と B.t は同一視される。キモい、と思われるかもしれないが、
+
+    let _ : A.t = MA1.x
+
+ではなく
+
+    let _ : M(Empty).t = MA1.x
+
+と書けたりするため、役に立たないわけではない。
+
+でも、キモい、 `A.t` と `B.t` は区別されるべき、という場合もあり、そういう functor
+は generative functor と言うそうな。まあ一回一回型が新しく生成されるわけだから generative なのかな。
+それが書けるようになった。
+
+    module A = Make()
+    module B = Make()
+
+と、`(Empty)` 代りに `()` と書くと `A.t` と `B.t` は別の型になって `A.x = B.x` は型エラーになる。
+`()` は空の structure にしか使えないので、非空の引数を取る functor に対して generative functor
+は作れないかというと、そういうことはなく、単にもう一つ `()` を引数に取る functor を作ってやれば
+よろしい。
+
+    module Make2(A : sig type t end)() = struct
+      type t = Foo
+      ...
+    end
+
+Applicative と generative の二つの functor が欲しいのは判るのだが、
+解決が cryptic で駄目なんじゃないかとおもう。Backward compatibility は大事だし新キーワード
+恐怖症も理解できるけど、もうなんか限界が来ているじゃないかと思わせる変更。
+
+
+Attributes and extension nodes
+------------------------------------
+
+Attribute ってのは簡単に言うと OCaml のコードの好きな所に好きなことを書けるのである。
+ほいでそれを好きにパースして利用するわけだ。パースされた AST を貴方がプログラムで好きに変換して
+OCaml コンパイラに戻す。変換次第でバニラ OCaml では出来無かった機能を追加できるわけ。 
+
+元々 OCaml でこういう機能追加は CamlP4 っていうかっちょいいプリプロセッサでやっていたんだけれども、
+これには幾つか問題があった:
+
+* 難しい。とにかく複雑。ドキュメント無いし
+* パーステクノロジが違う。OCaml バニラは lex+yacc だけど P4 はストリームパーサー。ただし文法拡張ができる。
+* 文法拡張するので拡張部分の事を知らないとプログラムが機械的に読み込めない。
+* プリプロセスの手間がかかりどうしても遅い
+
+で、この attributes では、もう文法拡張は止めましょう。その代り、 AST の好きなところにマークを
+付けれるようにしましょう。で、そのマークを元に AST を変更するフィルタを書きましょう、
+ということになった。
+
+これで嬉しいのは、 attributes を知らなくても少なくともコードのパースはできるってことで、
+エディタでのインデントとかそういう機能は P4 の文法拡張のことを気にしなくて良くなったってこと。
+
+Attribute は `[@...]` というキモい文法でコード中に埋め込むことができる。
+問題は、 `[@...]` だけでなく `[@@...]` や `[@@@...]` とかあることで醜いですねー。
+これは AST のどこに attribute がくっついているかを示すものらしい:
+
+* `[@...]` は expression とか
+* `[@@...]` は structure や signature item レベル
+* `[@@@...]` は多分、ソースコードファイル全体 (compilation unit) レベル
+
+覚え方は `@`(at) は ATtribute の頭文字。 
+
+例えば
+
+    let x = 1 [@attr]
+
+は `1` に対して `attr` という attribute がくっついているが、
+
+    let x = 1 [@@attr]
+
+は `let x = 1` という structure item に対して `attr` という attribute がくっついている。
+
+でこの attribute なんだけど、多分、メジャーな CamlP4 拡張は殆どこれでカバーできそう。
+だから P4 はさようならになると思われる。
+
+既にこの attribute は OCaml コンパイラ内部でも使われていて、`obj.mli` では
+
+    val final_tag : int [@@ocaml.deprecated]
+
+とか書かれている。 `final_tag` を使うと `ocamlc` が警告を出してくれる。
+
+あ、あと、 `[%...]` と `[%%...]` という extension てのもあるんだ。
+これは attribute と似ているんだけど、どっちかというと構文のキーワードを修飾して
+特殊な意味を持たせるのに使う。
+
+    [%io
+      let x = read in 
+      print x  
+    ]
+
+とか書いて、これを適切なパーサに通すと
+
+    read >>= fun x -> print x
+
+とかに変えてくれる、そんな感じ。で、 extension には簡単な書き方が用意されていて、
+上のはこう書いてもよい:
+
+    let%io x = read in
+    print x
+
+うーーーーーん、どうなんだろうねえ。これで Haskell の `do` 記法書かせて見栄えどうなるんかね。 
+実際、 lwt ライブラリの `bind` である `lwt x = ... in` は
+
+    let%lwt x = ... in
+
+と書かせたいみたいだけど、醜いよね。普通の `let` と `let%lwt` が混在してだらららーっ
+と並んでたら結構鬱になる気がするが…
+
+P4 が無くなるのは嬉しいんだけど、バニラ OCaml の syntax で何でもやるってには
+現在の OCaml の syntax ではまだ色々足りないような。
+
+LablTk が本体から消える
+----------------------------
+
+まあ、これ私が大分遊んだやつなんだけど、今更 Tcl/Tk とかねぇ。GUI は外に出しましょう!
+
 Camlp4 が本体から消える
-=============================
+------------------------------
 
 これは p4 が終わりということではなくて、別のレポジトリで管理されるということなんだけれども、
 もちろん、 p4 は終わりという意味です。
 
-LablTk が本体から消える
-=============================
-
-まあ、これ私が大分遊んだやつなんだけど、今更 Tcl/Tk とかねぇ。GUI は外に出しましょう!
-
 Open extensible types
-=============================
+-----------------------------
 
 まあコンストラクタが増やせる variant のようなもの。
 
 
 で実際の挙動はこうなっている
 
-    (* ocaml -dlambda *)
     type t = ..
     type t += A of int
-    type t += B
+    type t += B of int
+    
+    open Obj
+    
+    let parse_tag t = 
+      if t = int_tag then `int
+      else if t = string_tag then `string
+      else if t = lazy_tag then `lazy_
+      else if t = closure_tag then `closure
+      else if t = object_tag then `object_
+      else if t = infix_tag then `infix
+      else if t = forward_tag then `forward
+      else if t = no_scan_tag then `noscan
+      else if t = abstract_tag then `abstract
+      else if t = custom_tag then `custom
+      else if t = final_tag then `final
+      else if t = out_of_heap_tag then `out_of_heap
+      else if t = unaligned_tag then `unaligned
+      else `unknown t
+    
+    let tag_name = function
+      | `int -> "int"
+      | `string -> "string"
+      | `lazy_ -> "lazy"
+      | `closure -> "closure"
+      | `object_ -> "object"
+      | `infix -> "infix"
+      | `forward -> "forward"
+      | `noscan -> "noscan"
+      | `abstract -> "abstract"
+      | `custom -> "custom"
+      | `final -> "final"
+      | `out_of_heap -> "out_of_heap"
+      | `unaligned -> "unaligned"
+      | `unknown x -> string_of_int x 
+    
+    
+    let dump o = 
+      let open Format in
+      let rec dump ppf o =
+        let t = parse_tag @@ tag o in
+        match t with
+        | `int -> fprintf ppf "%d" @@ obj o
+        | `double -> fprintf ppf "%.4f" @@ obj o
+        | `string -> fprintf ppf "%S" @@ obj o
+        | _ -> 
+           fprintf ppf "[%s @[" @@ tag_name t;
+           let s = size o in
+           for i = 0 to s - 1 do
+             dump ppf (field o i);
+             fprintf ppf "@ "
+           done;
+           fprintf ppf "@]]"
+      in
+      eprintf "%a@." dump o
+    
+    let () = 
+      let o = repr (B 3) in
+      dump o
 
-    let x = [ A 3; B ]
+このプログラムの出力により `B 3` は
 
-    (* ocamlc -dlambda *)
+    [0 [object "B" 86 ] 3 ]
 
-    (let (A/1035 = (caml_set_oo_id (makemutable 248 "A" 0)))
-      (apply (field 1 (global Toploop!)) "A" A/1035))
-    (let (B/1036 = (caml_set_oo_id (makemutable 248 "B" 0)))
-      (apply (field 1 (global Toploop!)) "B" B/1036))
-    (let
-      (B/1036 = (apply (field 0 (global Toploop!)) "B")
-       A/1035 = (apply (field 0 (global Toploop!)) "A")
-       x/1037 = (makeblock 0 (makeblock 0 A/1035 3) (makeblock 0 B/1036 0a)))
-      (apply (field 1 (global Toploop!)) "x" x/1037))
-     
+というデータになっていることがわかる。つまり、第一要素に何かコンストラクタ名を含む
+オブジェクトタグのついたブロックを含む組だ。普通のバリアントと違ってタグは整数ではなくて
+このオブジェクトタグのついたブロックを使う。別モジュールでやはり `B` というコンストラクタを
+足した場合でもこの `[object "B" 86 ]` タグが作られるが、この二つのタグを区別できるように
+構造比較ではなくてアドレスを判別に使う。これは今までの例外の実装と同じだね。
+だから output_value や Marshal でデータを書き出し読み込んだ場合もとのコンストラクタとは
+同一視できなくなる。これも例外と同じ。
 
+4.01 では例外には object tag は使われていなかったんだけど、これはどういうことなんかいな。
+例えば上の関数で Failure "hello" はこうなってたんだけど:
 
+    [0 [0 "Failure" ] "hello" ]
+
+object に付いてくる hash で何か高速化狙ってるのかな? ちょっと不安を感じる…
+
+例外キャッチを match with に埋め込めるようになった
+-----------------------------------------------
+
+ただの糖衣構文。キモい。
+
+   let f lst = 
+     try 
+       match List.assoc "x" lst with
+       | None -> 0
+       | Some x -> x
+     with
+     | Not_found -> 0
+
+を
+
+   let f lst = 
+     match List.assoc "x" lst with
+     | None -> 0
+     | Some x -> x
+     | exception Not_found -> 0
+
+と書けるようになった。そこまでして短かく書きたければ、
+そもそも例外を発生させないライブラリ作りをするべきでは。キモい。
+
+まあ単なる糖衣のはずなので使って問題ないはず。