OCamlで自分で定義した演算子でOptionを楽して扱う
モダンなプログラミング言語には大体用意されているOption。 とても便利な機能ですが、ちょっと使い勝手が悪い(面倒くさい)場面が多々あります。
例えば、データベースからレコードを検索してきて、特定の年齢以上ならそのIDとユーザ名を出力する処理を考えます。
データベースの検索結果はoption
に包まれます。
type t = {
id: int;
age: int;
name: string;
}
(*DBから値を取得する的な関数*)
let find_by_id id : t option=
Some {id; age=35; name="Koji"}
(*35歳以上なら特殊な健康診断受けなさい*)
let is_required_special_medical_checkup (user: t option) =
match user with
| None -> None
| Some v -> if v.age >= 35 then user else None
(*user(type t)を表示する関数*)
let printer (user: t option) =
match user with
| None -> None
| Some v -> Some(Printf.sprintf "%s(%d)" v.name v.age)
let () =
let v = 10 |> find_by_id |> is_required_special_medical_checkup |> printer in
let () = match v with
| None -> ()
| Some v -> print_endline v;
in
print_endline "終了";
option
とmatch式を使って安全なコードを書けました。
しかし、関数is_required_special_medical_checkup
と関数printer
に、
NoneならNone、Someなら何かしてSomeに包んで返す
という全く同じ処理が含まれています。
今は関数が2つだけなので別に構いませんが、当然option
を扱うということはこのようなコードが至るところに出現してきます。
問題なのは、option
の値を扱うのに毎回match式を書かなければならないことです。
そこで、option
をもっとスマートに扱えるように、自身でオリジナルの中間演算子を定義します。
(* optionを便利に扱う中間演算子 *)
let (>>?) v f = match v with | None -> None | Some v -> Some(f v);;
上記のコードを使った実際のコードは以下の通り。
type t = {
id: int;
age: int;
name: string;
}
(* optionを便利に扱う中間演算子 *)
let (>>?) v f = match v with | None -> None | Some v -> f v;;
(*DBから値を取得する的な関数*)
let find_by_id id : t option=
Some {id; age=35; name="Koji"}
(*35歳以上なら特殊な健康診断受けなさい*)
let is_required_special_medical_checkup (user: t) =
if user.age >= 35 then Some user else None
(*user(type t)を表示する関数*)
let printer (user: t) =
Some(Printf.sprintf "%s(%d)" user.name user.age)
let () =
let v = Some 10 >>? find_by_id >>? is_required_special_medical_checkup >>? printer in
let () = match v with
| None -> ()
| Some v -> print_endline v;
in
print_endline "終了";
これで、関数is_required_special_medical_checkup
と関数printer
から、optionがNoneかSomeかどうかという判断が消えました。
実は、標準でOption.map
という関数も用意されてはいますが、型の定義を見れば分かる通り、渡す関数がOptionではない値を返すようになっています。
('a -> 'b) -> 'a option -> 'b option
もし処理の最初の値がSome
で、それ以降はNone
に変わらない、ということであればOption.map
を使うことも可能です。
公開日:2020/06/02