protos をちょっと使ってみる

昔みたいに Mixin のために何か細工する必要はなくなった。

http://d.hatena.ne.jp/shinichiro_h/20031208#p2

以下テストしてみたコード。

AP := Object clone do(
  ap := method(write("ap\n"))
  apa := method(ap())
  apb := method(ap())
)

A := AP clone do(
  a := method(write("a\n"))
  abc := method(a())
  ab := method(a())
  apa := method(a())
)

B := Object clone do(
  b := method(write("b\n")) 
  abc := method(b())
  ab := method(b())
  bc := method(b())
  apb := method(b())
)

C := Object clone do(
  c := method(write("c\n")) 
  abc := method(c())
  bc := method(c())
)

D := A clone
D appendProto(B)
D appendProto(C)

d := D clone

d a
d b
d c
d abc
d ab
d bc
d ap
d apa
d apb

D は A, B, C を継承していて、 A は AP を継承している。 D は A を継承するさい、 A を clone して(つまり最初にセットした proto は A) 、 appendProtoで B, C をこの順で追加している。つまり以下のような絵。

AP
 |
 A B C
 | | |
 +-+-+
   |
   D

結果は以下の通り。

a
b
c
a
a
b
ap
a
ap

メソッド探索の順序は、 A > AP > B > C ということだろう。まぁ普通。

writeln(D protos type)

などとすると List 。普通に foreach などが呼出せるから、かなりラクにリフレクションができそう。

私的 OCaml

http://d.hatena.ne.jp/shibacho/20050601/p7

id:shibachoさんとこなどを見て、私的に OCaml に思うところを。(以下完全に私見というか、私に OCaml がどう見えているか、です。)

OCaml は素晴らしい言語だと思います。私的にはちょっと、完璧すぎる。まず、とりまく環境から。

  • 安定感。コンパイラのバグなどあまり無さそう。なんかエライ人が数学的にどうこうらすい、っていう安心感。
  • 高速。 OCaml のせいで遅い、と感じたことはありませんし、各種ベンチマークも高得点です。
  • デバッグなど。普通にできます。
  • プロファイルなど。普通に gprof でできてしまいます。
  • OCamlMakefile がちょっと便利すぎる。 (参考までに tello の Makefile の簡潔さを見てくださいませ)
  • タダものじゃないプリプロセッサ、camlp4。(構文を理解している!)
  • コンパイラ/バイトコード/インタープリタと好きな実行法を選択できる。
  • それなりにそろってる標準ライブラリ。
  • 普通に動作環境多い。(tello は Win32 バイナリ同梱ですが、 WINE でコンパイルして WINE でテストしかしていなかったり)
  • 私的には IDE とかいらないから無くていい。

言語としては。

  • GC
  • 型推論 & えらい強い型付け。
  • 多相。
  • lambda & curry
  • 副作用代入可能
  • 手続き的記述可能
  • OOとか例外とか使えるらしいけどどうでもいいや。

こっちは詳しく説明していきます。 GC と最後の項目はまぁいいとして。

型推論。 C とかの感覚だと、 int とか書くのはめんどくないょ、と思いますが、 C++ 屋からしてみると std::map とか激しくめんどいです。 STL 使いには重要な関数ポインタやファンクタはなおめんどい。正直、何をどうやったら型推論なんて実装できるのかわかってませんが、ちゃんと動いているから感心。

えらい強い型付けというのは、例えば int と float が暗黙の変換をしてくれない、とか。「コンパイルに通ってしまえばバグはほとんどない」みたいな表現は誇大広告ではありますが、全くの嘘ではないです。

多相。型推論とセットでこそ、という機能ですが、 C++ でいう template をわざわざ書かなくて良い感じ。ただし C++ みたくメタプロの道具になったりはたぶんしません。

lambda & curry 。これも型推論のおかげでずいぶんラクになっている感があります。例えばオセロだと、プレイヤーが置くロジックをいろいろさしかえたいわけでして(ユーザがマウスで操作、思考ルーチン、など)、例えば盤面を渡したら (x,y) を返すような関数をさしかえるわけです。 C 的にイメージを書くとこんな感じ。

point gui_think(board b) {
  // ...
  return (x, y);
}

point cpu_think(int level, board b) {
  // ...
  return (x, y);
}
int CPU_LEVEL = 3; // CPU の強さ
point cpu_think_default(board b) {
  return cpu_think(CPU_LEVEL, b);
}

typedef point Algo(board);
Algo[] algorithms = [gui_think, cpu_think_default];

型推論のおかげで、余計なものはあまり書く必要がありません。例えばこんな感じになるのかな。

gui_think(b) {
  // ...
  return (x, y);
}

cpu_think(level, b) {
  // ...
  return (x, y);
}
CPU_LEVEL = 3; // CPU の強さ
cpu_think_default(b) {
  return cpu_think(CPU_LEVEL, b);
}

algorithms = [gui_think, cpu_think_default];

lambda のおかげで、例えば一時的なロジックなんかも気軽に書けます。

algorithms = [gui_think,
 lambda(b) {
  if (hogehoge) return cpu_think_default(b);
  else // another algorithm...
 }
];

CPU_LEVEL がどうにもダサすぎるわけで、これでは例えばレベルの違う CPU 同士の対戦はできません。どうにかして動的なパラメタ化したいのですが、 curry があったらカンタン。

gui_think(b) {
  // ...
  return (x, y);
}

cpu_think(level, b) {
  // ...
  return (x, y);
}

algorithms = [gui_think, cpu_think(3)];   // curry!

最後の行のように、部分的に引数を渡して、引数が一つ少ない関数を作るようなことができます。最初のサンプルコードに比べて、いかにめんどうな記述が減ったかはすぐに見て取れると思います。実際 tello のコードはこれに近い感じです。

……が、記述は OCaml 式です。つまりなんというか、 C 系列とはあまり似てません。結局ここが一番のボトルネックなのかな、と思います。

まぁ私的欠点は次にまとめるとして、副作用的代入可能だの手続き的記述可能、について。その C 系列とはあまり似ていない文法に慣れてしまえば、 C 系列になれている人にとってはさして苦労なく、自分が書いているプログラム的なものを書けてしまいます。副作用、ってのはようするに代入とかのことですが、これがまぁ特に考えることなくできてしまいます。純粋な関数型言語とやらは初期化以外の代入は許されてないらしいですが、やっぱりこれは私的には移行のさいの大きな障壁の一つです。別に普通に手続き的に処理を並べていっても動くのも、移行の観点からはありがたいです。末尾再帰せずとも、普通に for や while が使えるのはありがたい。極まってる人からすると眉をしかめるところなのでしょうが、理想的な形のコードを、まさに今、自分の能力で書けないような時には、日和れることはありがたいと思うのです。このへんは Haskell あたりとの大きな違いな気がします。

で、最後に欠点。

  • マイナー気味なので情報が少ない。
  • 標準以外のライブラリが少ない。
  • なんかやってる人が賢い人が多い気がする(偏見)せいか、「オバカ」な情報が少ない。
  • 猛威をふるっている C 系列と文法がかなり遠い。
  • コンパイルエラーのメッセージがどうもわかりにくい。(文法に慣れていないこととあいまって、単純な文法エラーに案外悩む)

ま、そんな感じです。

追記:

http://www.kmonos.net/wlog/51.php#_2031050602

あーすっかりサックリパターンマッチ&Variantを忘れてました。なんか後少しで私に染みつきそうなものの、まだ脳内に取り込めてないので「私的」OCaml としてはなかったのはそれはそれで良しかしらん。

それと欠点に一つ追加。

  • 常に、「よくわかってる人は今俺が書いてる方法より簡潔な記述できるんだろなー」という思いがつきまとう。

もうちょっと追記:

型推論のところ、 C で int を書くのはめどくない、って書こうとして、めどいと書いてしまっていたので修正…

昔の記述を見るに3年くらい前からたまに思い出したように触って感心する言語。それが OCaml 。当時は for とか使ってる模様。

http://shinh.skr.jp/misc/index.html?2002101601#2002101601

なんとなく、最近触り始めている、 Scheme やら ActionScript も私見を書いてみたいところ。

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h