OCaml で、配列の要素になんか関数を適用して、 その関数が成功したらその結果を返す、という関数を書きたいことがあった
これは高階関数で、'a array 型の配列と、 その要素を受け取って 'b option 型の値を返す関数 f を受け取る。 配列の最初の要素から順に f に与えて、f が None を返したら次の要素に進む。 最初に Some x を返したらそれを返す、というものである。 なお、最後まで Some が返されなかったら None を返す。
let array_find_map (f : 'a -> 'b option) (s : 'a array) : 'b option = ...
さて、どうやって実装するか
配列なのでとりあえず for ループでやるか、と考えて、ループの途中で脱出するのは例外を使えばいいだろう、 ということで、以下を書いた
let array_find_map (f : 'a -> 'b option) (s : 'a array) : 'b option =
  let exception Found of 'b option in
  try
    for i = 0 to Array.length s do
      let v = f s.(i) in
      match v with
      | None -> ()
      | Some _ -> raise (Found v)
    done;
    None
  with Found v ->
    v
しかしこれはエラーになる
Error: The type variable 'b is unbound in this type declaration.
どうも、例外の引数は多相型にはできないぽい
まぁ、再帰を使えば例外を使わずに書けるからいいけど
let array_find_map (f : 'a -> 'b option) (s : 'a array) : 'b option =
  let n = Array.length s in
  let rec aux i =
    if i < n then
      let v = f s.(i) in
      match v with
      | None -> aux (i+1)
      | Some _ -> v
    else
      None
  in
  aux 0
でも、ループで書けないのは気になる。 検索すると、Locally abstract types なるものを使えるようだ
The OCaml Manual, Locally abstract types
以下のようにしたら通った
let array_find_map (type a b) (f : a -> b option) (s : a array) : b option =
    let module M = struct exception Found of b option end in
    try
      for i = 0 to Array.length s do
        let v = f s.(i) in
          match v with
        | None -> ()
        | Some _ -> raise (M.Found v)
      done;
      None
    with M.Found v ->
      v
しかし、これが通るんならなんで最初のが通らんのだ、意味もなく module が必要でよろしくない、 と思ったが、試すと module を使わなくてもいいようだ
let array_find_map (type a b) (f : a -> b option) (s : a array) : b option =
    let exception Found of b option in
    try
      for i = 0 to Array.length s do
        let v = f s.(i) in
          match v with
        | None -> ()
        | Some _ -> raise (Found v)
      done;
      None
    with Found v ->
      v
とすると、最初のが通らないのは、単に型変数のスコープから外れているから、というだけの話なのかな
[latest]