OCaml で Abstract モジュール型を実現する方法

@asya_aoi1049 on Sat May 28 2022
6.6 min

目次

概要

オブジェクト指向でいうところの、抽象基底クラスを実現する方法。

なお、OCaml では オブジェクトも扱えるが、ここでは ABC モジュールの実現を考えてみる。

このdiscuss を見て、なるほどとなったのでまとめてみたというのが背景。

抽象基底クラスの理解については、Python の この章
を参考にした。

ABCモジュール型の定義

ABCモジュールを以下のように定義する。
ここでは、ABCの頭文字を取って、Aモジュール型とした。

※いやー、OCamlにはモジュール型(sig..end)とモジュール(struct...end)があって混乱しますね…
というわけで、これらについてはOCamlのモジュール (ストラクチャ) とモジュール型 (シグネチャ)
を参考にしてください。

module type A = sig
  type t
  type err
  type 'a do_things = t -> ('a, err) result
end

Aモジュール型は、型tと型err、任意の型で表現される'a do_thingsを持つ。

tはAモジュール型が扱う型の総称と思ってもらえれば良い(と思う)。

errは、その名の通りAモジュール型が定義するエラー型。

'a do_thingsは、ちょっと複雑。なにせ、型名が'a do_thingsだ。

そもそも、'a とは何かというと、多相を表す。多相とは何かというと、任意の型を取れるよということ。

つまり、string do_thingsでもint do_thingsでも良い。

で、'a do_things型は、t型をとり、('a, err) result('a, err)で表現されるresult型)を返す。

多分、ABCモジュール型は抽象的すぎて具体例がないとイメージが沸かないと思うので、ABCモジュール実装を利用する例を次に見ていく。

ABCモジュールの定義

例えば、ABCモジュール型を利用したMy_aモジュールを考えてみる。

module My_a : A with type t = int and type err = string = struct
 type t = int
 type err = string
 type 'a do_things = t -> ('a, err) result 
end

My_aは、Aモジュール型が持つ型tint、型errstringと定義した
ものだ。

ABCモジュールの利用

さらに、ここでAモジュール型に値を追加した、別のCモジュール型を考えてみる。

module type C = sig
  module A: A
  val of_int: int -> A.t
  val make: string A.do_things
end

module A: A はAモジュール型(コロン:より右側)をAという名前で扱いますよ、という宣言。

※ 表現が回りくどいよ!まぁざっくり言うとAモジュール型を使いますよってことでOK。

Cモジュール型は、of_int (int型の値を引数にとり、A.t型を返す)とmakestring A.do_thigns型を返す。
stringはAモジュール型の'aに相当)を定義したものだ。

さて、Cモジュール型を定義したことで、具体的にAモジュールの型を利用する準備が整った。

以降では、このCモジュール型を利用する具体例を見ていく。

Cモジュールの利用

いよいよ、上記で定義したモジュールを利用する時が来た。

module My_c: C = struct
  module A = My_a
  let of_int x = x
  let make x =
    print_endline (string_of_int x) ;
    Ok "ok"
end

module A = My_a はCモジュール型内で利用するモジュールAを、My_aと見立てて、
(つまり、Aモジュール型で宣言されていたtint型、errstring型で)扱う。

of_intは、x(これはCモジュール型によるとint型であることがわかる)を引数にとり、x(これはCモジュール型によるとA.tであることがわかる)を返す。

makex(これはMy_aモジュールによればt型)を取り、Ok "ok"(これはCモジュール型によれば(string, err) result)を返す。

makeの実行例は以下のような感じ。

My_c.make (My_c.of_int 1)

(* 結果 *)
1
- : (string, My_c.A.err) result = Ok "ok"

参考

日別に記事を見る