もなどを、真似したい。

もなど(くらす)を真似したいと、願うのです。

return :: Monad m => a -> m a
bind :: Monad m => m a -> (a -> m b) -> m b

みたいに type constructor が abstract されているのを OCaml の枠内で出来るだけ簡単に表現したいのです。

やりたいのは type class の真似事です:

(+) :: Num a => a -> a -> a
(-) :: Num a => a -> a -> a
double :: Num a => a -> a

はそういうのが無いので、

type 'a num
val (+) : 'a num -> 'a -> 'a -> 'a
val (-) : 'a num -> 'a -> 'a -> 'a
val double : 'a num -> 'a -> 'a

に簡単にエンコードできます。num は dictionary の実装で何でもいいんですが、t num の値から t 用の (+) と (-) が取り出せるようになっている。例えば、

type 'a num = {
  plus : 'a -> 'a -> 'a;
  minus : 'a -> 'a -> 'a;
}
let (+) num = num.plus
let (-) num = num.minus
let double num x = num.plus x x
let int_num : int num = { plus = Pervasives.(+); minus = Pervasives.(-); }
let float_num : float num = { plus = (+.); minus = (-.) }

Dispatch は手動。(+) float_num 1.2 3.4 と書けば良い。手動が嫌だったら float_num の適用を省略できるように OCaml を少し改造すれば良いです。(+) 1.2 3.4 をもらうと (+) float_num 1.2 3.4 にプログラム変換してくれるようになる。この改造は、出来ました。

同じエンコーディングMonad クラスでやろうとすると、

type 'm monad
val return : 'a -> 'a 'm
val bind : 'a 'm -> ('a -> 'b 'm) -> 'b 'm

になるのですが、'a 'm みたいな物は OCaml に無い。困ったね。(Monad クラスの様に、data type で抽象化した type class を constructor class と言うそうです。) モジュールにしてやれば 'm の部分を抽象化することは出来ますけど、

module type Monad = sig
  type 'a m
  val return : 'a -> 'a m
  val bind : 'a m -> ('a -> 'b m) -> 'b m
end

module Option : Monad with type 'a m = 'a option
module List : Monad with type 'a m = 'a list

これを上の (+) や (-) みたいに引数を一つ多くした関数にしてみると、

(* こんなんは今は書けない。<Monad> は Monad 型を持つ module の module value だと思って *)
val return : <Monad> -> 'a -> 'a Monad.t
val bind : <Monad> -> 'a Monad.t -> ('a -> 'b Monad.t) -> 'b Monad.t

で、first class module が必要になってくる。3.12 まで待つ?まあ、もしあったとすると、こんなプログラムが書ける:

(* <Option> は Option module を module value にした物だと思って *)
bind <Option> (Some 1) (fun x -> return <Option> (x + 1))

で、やりたいことは、この <Option> の適用をコンパイラに推測させて自動化させたいのですが、、、Monad の正体が判ってない時点で、例えば、Monad.t と option は同じであると unification させる事になります。あれ?やっぱ Monad.t は変数扱いになってる。上の 'm と同じじゃん。

という訳で type name を abstract するための変数を入れる改造が必須ということなのかな、、、できるだけ、おチープにやりたいのですが、、、

まあ、今の枠組でも、こういう風に書けなくもないのですが、、、

type ('a, 'am, 'bm) monad
 (* 'am は 'a 'm が書けないのでそれを包含する OCaml で valid な型 *)
val return : ('a, 'am, 'bm) monad -> 'a -> 'am
val bind : ('a, 'am, 'bm) monad -> 'am -> ('a -> 'bm) -> 'bm

これは型が general 過ぎて、悲しい。

もなどくらすを真似したいと、願うのです。