もなどを、真似したい。
もなど(くらす)を真似したいと、願うのです。
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 過ぎて、悲しい。
もなどくらすを真似したいと、願うのです。