2022/01/17

OCamlのモジュールはファーストクラス

ファーストクラスとは

プログラミング言語でよく目にするファーストクラスという言葉。例えば某言語で関数はファーストクラスだ、とすると、その言語では関数を変数に格納できたり、関数のパラメータとして渡すことができたり、関数の戻り値として返すことができたりするということです。

OCamlにおいて、モジュールはファーストクラスですので、上記のようなことが可能です。例えば変数に格納する場合は、まずモジュールを作成して、


# module A = struct let x = 1 end ;;
module A : sig val x : int end
# let a = (module A) ;;
Error: The signature for this packaged module couldn't be inffered.

はい、エラーとなりました。「このパッケージモジュールのシグネチャーを推測できない」と。

ではシグネチャーを追記しましょう。


# module type Int_type = sig val x : int end ;;
module type Int_type = sig val x : int end
# let a = (module A : Int_type);;
val a : (module Int_type) = <module>
# a ;;
- : (module Int_type) = <module>

格納することができました。せっかくなので、もう一つ作りましょう。


# module B = struct let x = 2 end ;;
module B : sig val x : int end
# let b = (module B : Int_type);;
val b : (module Int_type) = <module>
# b ;;
- : (module Int_type) = <module>

シグネチャを明示的に書くことで、変数に格納できることが分かりました。今度は関数に渡してみましょう。

ファーストクラスモジュールの内容にアクセスするには、通常のモジュールに戻す必要があります。


# module A' = (val a : Int_type) ;;
module A' : Int_type
# A'.x ;;
- : int = 1

これを応用して、関数を作ってみると、以下のようなものになります。


let add x y =
  let module X = (val x : Int_type) in
  let module Y = (val y : Int_type) in
  X.x + Y.x

これを実際に使ってみると、


# add a b ;;
- : int = 3

この関数をちょっと修正してみましょう。let module .....というところが重複しているので、そこだけ切り出して別の関数にしましょう。


let to_int m =
  let module M = (val m : Int_type) in
  M.x
  
let add x y = to_int x + to_int y

これでコードがちょっとスッキリしました。

また、戻り値の型をInt_typeにしたい場合は、上記のadd関数を以下のようにします。


let add x y =
  (module struct
    let x = to_int x + to_int y
  end : Int_type)

実際に使ってみると、


# let c = add a b ;;
val c : (module Int_type) = <module>
# module C = (val c : Int_type) ;;
module C : Int_type
# C.x ;;
- : int = 3

となります。またちょっと省略して


# module C = (val add a b : Int_type) ;;
module C : Int_type
# C.x ;;
- : int = 3

という形でも大丈夫です。

また、ファーストクラスということは配列やリストの要素にもできるということなので、それらも試してみます。


# let arr = [|a; b|] ;;
val arr : (module Int_type) array = [|<module>; <module>|]
# let ls = [a; b] ;;
val ls : (module Int_type) list = [<module>; <module>]

問題なくできました。配列やリストにできるということは、ArrayモジュールやListモジュールが使えるということです。

まとめ

OCamlのモジュールはファーストクラスであり、ひと手間あるものの、他の整数や文字列などのようなものと同じように扱うことができる。