HaskellのMaybeモナドとかListモナドによるバックトラックとかは大変有用。 でも欲張ってモナドそのものをOCamlに導入しようとすると、色々と複雑になってくる。 そこで、camlp4を使ったモナド拡張を利用して、シンプルにモナドを楽しんでみる。
monad拡張をダウンロードしてきたら、おもむろにmake。その後、
$ocaml -I +camlp4 Objective Caml version 3.09.3 #load "camlp4o.cma";; Camlp4 Parsing version 3.09.3 #load "pa_monad.cmo";;
として準備OK
まず定義。超簡単。
module MaybeM = struct let bind m f = match m with None -> None | Some v -> f v end;; module MaybeM : sig val bind : 'a option -> ('a -> 'b option) -> 'b option end
使ってみる。performというのがmonad拡張のキーワード。 bind関数を呼び出してくれる。
# let env key = try Some (Sys.getenv key) with _ -> None in let name = perform with module MaybeM in user <-- env "USER"; host <-- env "HOST"; Some (user ^ "@" ^ host) in match name with None -> "None\n" | Some n -> n ^ "\n";; - : string = "ogasawara@blendy\n"
これでoption型も怖くない。
Haskellのlistモナドと同等・そのまんま。
# module ListM = struct let return x = [x] let bind m f = List.flatten (List.map f m) let guard c = if c then return () else [] end;; module ListM : sig val return : 'a -> 'a list val bind : 'a list -> ('a -> 'b list) -> 'b list val guard : bool -> unit list end
バックトラックしてくれる。
# perform with module ListM in i <-- [1; 2; 3]; j <-- [4; 5; 6]; ListM.guard (i + j > 7); ListM.return (i, j);; - : (int * int) list = [(2, 6); (3, 5); (3, 6)]
応用すると、OCamlには参照型もあるので、条件に合う中で最大のものとかも簡単に作れる。