Skip to content

Latest commit

 

History

History
149 lines (111 loc) · 4.16 KB

include.md

File metadata and controls

149 lines (111 loc) · 4.16 KB

呪文 include module type of struct include X end

(mli と sig の関係を判っている人向け用)

モジュールを拡張することってよくある:

module Base : sig
  type t = Foo
end = struct
  type t = Foo
end

module Ext = struct
  include Base
  let f Foo = ()
end

問題は、拡張したモジュールのインターフェース(mli)書くのが面倒臭いってこと。

上の Extocamlc -c -i によれば、こんな sig 持っている:

module Ext : sig 
  type t = Base.t = Foo 
  val f : t -> unit 
end

さて基本モジュールの sig が大きい場合、これを手で書くのは大層辛いのだが、 こう書くことができる:

module Ext : sig
  include module type of struct include Base end
  val f : t -> unit 
end

何の事かちょっと判らないが、実際これは ocamlc -c -i してやると A と同じ 結果が出て来るのである。

これが、

module Ext : sig
  include module type of Base
  val f : t -> unit 
end

ではいけない。これは

module Ext : sig 
  type t = Foo        (* Ext.t と Base.t との関係は隠蔽されてしまった *)
  val f : t -> unit 
end

になる。 Base.tExt.t は別の型と看做されるので BaseExt を混用することができなくなる。

なぜこうなるか

こうなっているらしい。

module Base = struct
  type t = Foo
end

module Copy = Base

module Included = struct include Base end

こいつらはこんな感じの sig を持つ:

module Base : sig
  type t = Foo
end

module Copy = Base

module Included : sig
  type t = Base.t = Foo
end

Copy の型 module Copy = Base は 4.02.x から入ったもので、 CopyBase の エイリアスですよ、ということを表す。まあこれはエイリアスを多用するこのごろのライブラリの為の最適化で、 内容的には Included の sig と同じで type t = Base.t = Foo を持っていると思ってよい。

さて、Base を include することで拡張した Ext の型を module type of X で 表現しようとすると結果どうなるか、次のコードでわかる:

module Ext = struct
  include Base
  let f Foo = ()
end

module ExtBase     : module type of Base = Ext
module ExtCopy     : module type of Copy = Ext
module ExtIncluded : module type of Included = Ext

これを ocamlc -c -i してやると:

module Ext : sig 
  type t = Base.t = Foo 
  val f : t -> unit 
end

module ExtBase : sig 
  type t = Foo      (* Ext.t と Base.t との関係は隠蔽されてしまった *)
end

module ExtCopy : sig 
  type t = Base.t = Foo 
end

module ExtIncluded : sig 
  type t = Base.t = Foo 
end

ごらんのとおり module type of Base で制限すると ExtBase.tBase.t との 関連性を失う。CopyIncluded では関連性は保たれたままある。

たいそう気持悪いのだがこれが module type of X の仕様である。 module type of XX の sig をそのまま持って来る。 module type of Base であれば type t = FooBase の sig であるので、それが入ってくる。この際 tBase.t であったとかは 自動的に入らない。Included および Copy はその sig に t が実は Base.t と 同じである事が type t = Base.t = .. と情報として含まれているので module type of Includedmodule type of Copy にも受け継がれる、ということになる。

module type of BaseBase とは関係が無い sig になるというのはどうも私は気持ち悪いが、 そのような sig が作りたくなる場合もあると思われるので理解できる。

一方 Base と関連性を保ったままの Base互換の sig は module type of struct include Base end というお経のようなコードを書かなくてはいけない のが辛い。覚えられない。やはり一時的に隠し機能としてあった (module Base) とか書けないですかね。