OCaml 4.08のbinding operator


はじめに

OCaml 4.08よりbinding operatorというものが追加されました。

8.24 Binding operators

簡単にいうとこんなかんじ

(* val ( let* ) : 'a option -> ('a -> 'b option) -> 'b option *)

let (let*) x k =
match x with
| None -> None
| Some v -> k v

そうだね、モナドだね


HaskellでMaybeなMonad

-- OCamlっぽく書くと

(>>=) x k =
case x of
Nothing -> Nothing
Just v -> k v

OCamlにはHaskellのようなdo記法がありませんが、binding operatorのおかげで、x <- e; klet束縛とほぼ同じ構文で書けるようになる。最高


binding operator

letはそもそもこんな感じ

let (x : t) = e1 in (e2 : t')

(* || *)
((fun x -> e2) : t -> t') (e1 : t)

これをbinding operatorで実装するとこうなる。

let (let*) : t -> (t -> t') -> t'

= fun x k -> k x

let* x = e1 in e2

e2xを受け取って計算をおこなう関数と考えるとスッと入ってくると思います。

もっと直球でいうとe2xがholeになっている継続なんですねぇ…。


bindしないbinding operator

ただの演算子というか変数なのでどう定義しても怒られない。

let (let*) = 3

print_int (let*) (* prints 3 *)

bindの形で使うと型エラーで怒られる。

let (let*) = 3

let* x = 5 in x + 4
(*
Line 1, characters 0-4:
Error: The operator let* has type int but it was expected to have type
'a -> ('b -> 'c) -> 'd
*)

実はOCaml4.08.0のコンパイラ/インタプリタではエラーメッセージも改善されたのでどこが間違ってるのかわかりやす〜い

ともかく、binding operatorをbindingの形で使うには第2引数が関数であることが必須のようだ。


継続の利用

listでもうすこし雰囲気を出しますか。

let (let+) x k = List.(map k x |> flatten)

let return x = [x]

let+ x = [1; 2; 3] in
let y = x + 4 in
let z = y + 5 in
return z
(* int list = [10; 11; 12] *)

継続が触れるのでletのbody部分を何回も走らせられるのがいいよねえ。


継続の利用2

継続が使えるんで何でもできる。

とりあえずGoのdeferが実装できてしまう。

(* val ( let*> ) : (unit -> 'a) -> (unit -> 'b) -> 'a *)

let (let*>) th k = k (); th ()

let*> defer() = print_int 2 in
let*> defer'() = print_int 4 in
print_string "the answer is "
(* prints "the answert is 42" *)

bindeeのサンクを受け取り、bodyを先に走らせてあとでbindeeを実行する。

Goのdeferでこんなんでいいんだっけ、とにかく後処理を登録することができるようになる。


継続の利用3

なんかいろいろ書ける。

let (let||) (x, y) (k : int -> 'a) : 'a list =

Array.(make (y - x + 1) 0 |> mapi (fun z _ -> k (x + z)) |> to_list)

let|| x = (1, 10) in x * x
(* int list = [1; 4; 9; 16; 25; 36; 49; 64; 81; 100] *)


おわりに

継続が使えるって最高…。