在 OCaml 中,给模块起别名到底做了什么?

In OCaml, what does aliasing a module do exactly?

在 OCaml 中,要将另一个模块引入作用域,您可以使用 open。但是像这样的代码呢:

module A = struct
  include B.C

  module D = B.E
end

这是否会创建一个与 B 创建的模块无关的名为 A 的全新模块?或者 B 中的类型是否等同于这个新结构,例如 A.t 中的类型是否可以与 B.C.t 中的类型互换使用?

特别是,与 Rust 相比,我认为这与编写类似

的东西有很大不同
pub mod a {
  pub use b::c::*;
  pub use b::e as d;
}

是的,module A = struct include B.C end 创建了一个全新的模块并导出了 B.C 中的所有定义。从 B.C 导入的所有抽象类型和数据类型都与该模块明确相关。

换句话说,假设你有

module Inner = struct
  type imp = Foo
  type t = int
end

所以当我们导入 Inner 时,我们可以访问 Inner 定义,

module A = struct
  include Inner
  let x : imp = Foo
  let 1 : t = 1
end

A中的Foo构造函数与Inner模块中的Foo构造函数属于同一类型,因此下面的类型检查,

A.x = Inner.Foo

换句话说,include不是单纯的copy-paste,而是这样的,

module A = struct 
  (* include Inner expands to *)
  type imp = Inner.imp = Foo
  type t = Inner.t = int
end

这种保留类型相等性的操作正式称为 strengthening 并且 always 在 OCaml 推断模块类型时应用。换句话说,类型系统永远不会忘记类型共享约束,删除它们的唯一方法是显式指定不公开共享约束的模块类型(或使用 module type of 构造,见下文)。

例如,如果我们将定义一个模块类型

module type S = sig 
  type imp = Foo
  type t = int
end

然后

module A = struct 
  include (Inner : S)
end

会生成一个新的类型foo,所以A.Foo = Inner.Foo将不再进行类型检查。使用显式禁用模块类型强化的 module type of 构造也可以实现相同的效果,

module A = struct
  include (Inner : module type of Inner)
end

将再次生成不同于 Inner.FooA.Foo。请注意 type t 在所有实现中仍然兼容,因为它是 清单类型 并且 A.t 等于 Inner.t 不是通过共享约束而是因为两者都等于 int.

现在,你可能会有疑问,

module A = Inner

module A = struct include Inner end

答案很简单。在语义上它们是等价的。而且,前面的并不是你想象的模块别名。两者都是 模块定义 。两者都将定义一个具有完全相同模块类型的新模块A

模块别名是存在于(模块)类型级别的特征,即在签名中,例如,

module Main : sig 
  module A = Inner (* now this is the module alias *)
end = struct 
  module A = Inner
end

所以模块别名的意思是,在模块级别,A 不仅与 Inner 具有相同的类型,而且它正是 Inner 模块。这为编译器和工具链打开了一些机会。例如,编译器可能会避开模块复制并启用模块打包。

但这一切都与观察到的语义无关,尤其是与打字无关。如果我们忘记显式相等(再次主要用于更优化的模块打包,例如,在沙丘中),那么模块的以下定义 A

module Main = struct 
  module A = Inner
end

与上面使用模块别名的完全相同。使用先前定义键入的任何内容都将使用新定义键入(模模块类型别名)。它一样强大。和下面一样强,

module Main = struct 
  module A = struct include Inner end
end

甚至以下,

module Main : sig 
 module A : sig 
   type imp = Impl.imp = Foo
   type t = Impl.t = int
 end
end = struct 
  module A = Impl
end