Arantium Maestum

プログラミング、囲碁、読書の話題

モジュールシグニチャの多重継承

前回の記事

module type ADD = sig
  type t
  val add : t -> t -> t
  val to_string : t -> string (* ここ *)
end

のようなシグニチャを書いたが、書いていて「うーん、これはおかしいな」と思った。

MULto_stringが必要だったからといってADDのインターフェースに追加するような関数ではない。

本当は

module type ADD = sig
  type t
  val add : t -> t -> t
end

module type PRINT = sig
  type t
  val to_string : t -> string
end

module MakeMul ( M : ???) = struct
...
end

と書きたかったのだが、ここでMADDでもありPRINTでもあると表現する???部分に当たるシグニチャの書き方に詰まってしまった。OOPでいうところのインタフェースの多重継承にあたる機能・構文である。

OCamlのモジュールシステムで継承といえばincludeなのだが、例えばこういうのはうまくいかない:

module MakeMul (M : sig
  include ADD
  include PRINT
end) = struct
...
end

Error: Illegal shadowing of included type t/??? by t/???などとエラーが出る。ADDtype tが定義されるのをincludeした後にPRINTでも同じくtype tincludeされて衝突してしまう。

そこでdestructive substitutionの出番だ!と

module MakeMul (M : sig
  include ADD
  include PRINT with type t := ADD.t
end) = struct
...
end

としてみたのだがError: Unbound module ADDと怒られてしまう。ストラクチャ(モジュール実装)の方だとtype t := ADD.tでいけると思うのだが、シグニチャだとADD.tのようにアクセスできないのが問題。

もうちょっと調べてみたところ:

stackoverflow.com

このStackOverflow記事を見つけ、そこでこのReal World OCamlの箇所について知った:

dev.realworldocaml.org

すでにinclude ADDしてるのでinclude PRINT with type t := tでいい:

module MakeMul (M : sig
  include ADD
  include PRINT with type t := t (* 二番目のtはADDから来ている *)
end) = struct
...
end

include PRINT with type t := tが何をしているかというと、PRINTシグニチャに出てくるtへの言及を現在のスコープにあるtに置き換えた上でPRINT内のtype tという定義を消し去ってしまう。これでto_stringADDに定義されているtからstringへの関数になる。

しかしRWOで書かれている通り、確かに:

module MakeMul (M : sig
  type t
  include ADD with type t := t
  include PRINT with type t := t
end) = struct
...
end

の方がADDPRINTも統一的に扱っていていい感じだ。こちらを使っていきたい。

ちなみに

module MakeMul (M : sig
  type t
  include ADD with type t = t
  include PRINT with type t = t
end) = struct
...
end

type t = tとしてしまうと

Error: Multiple definition of the type name t.
       Names must be unique in a given structure or signature.

とエラーになる。includeで継承する時は大抵=ではなく:=でdestructive substitutionを使う必要がある。

最初に知った時は「なんだこの変な機能は」と思ったものだが、destructive substitutionは意外と非常に便利な機能である。