OCaml 4.08 から binding operator が導入され let* などが定義できるようになりました:
let foo x f = (* なんやかんや *)
let (let*) = foo
let _ =
let* x = call_something hoge in
piyo
(* 上記の式は foo (call_something hoge) (fun x -> piyo) と等価 *)
パッと見、何に使うのか良くわからない[1]の機能ですが Result.bind に割り当てておくと Rust の try![2] みたく使えて便利です:
let f1 x = Ok () (* なんやかんややって result を返す関数 1 *)
let f2 x = Ok () (* なんやかんややって result を返す関数 2 *)
let (let*) = Result.bind
let _ =
let* x2 = f1 x1 in
let* x3 = f2 x2 in
(* x3 でなんやかんや *)
まず f1 を呼び、それが失敗したら early return して、成功したら f2 を呼び、それが失敗したらやはり early return して、成功したらその結果を使って何かする、というコードが直感的に書けます。let* が無いと以下のように match with をネストしないといけないので大変です:
let _ =
match f1 x1 with
| Error _ as e -> e
| Ok x2 ->
match f2 x2 with
| Error _ as e -> e
| Ok x3 ->
(* x3 でなんやかんや *)
さて毎回 let* を Result.bind に割り当てるのはまぁまぁ面倒ですが、OCaml 5.4 から追加された Result.Syntax にはこの定義がそのまま入っています。ということで let open Result.Syntax in すればどこでも使えるようになりました。もっと手軽にするのであれば dune ファイルに以下のように書いておくと全てのモジュールでデフォルトで open Result.Syntax されるようになります:
(env
(_
(flags
(:standard -open Result.Syntax))))
ちなみにリファレンスを見ると Result.Syntax には let* の他に let+ が定義されています。let+ は Result.map に割り当てられているため in 以下で result ではなく中身の値を直接返したいときに使えます。また let*・let+ と組み合わせて使えるように and*・and+ も定義されています。