显微镜中的模块比看起来更微妙
(如果您的眼睛在某个时候发呆,请跳到第二部分。)
让我们看看如果将所有内容放在同一个文件中会发生什么。这应该是可能的,因为单独的计算单元不会增加类型系统的能力。 (注意:对于文件a.* 和b.* 的任何测试,请使用单独的目录,否则编译器将看到编译单元A 和B,这可能会造成混淆。)
module A = (struct
type t = { x : int }
let b = B.a
end : sig
type t = { x : int }
val b : t
end)
module B = (struct
let a : A.t = { A.x = 1 }
end : sig
val a : A.t
end)
哦,好吧,这行不通。很明显这里没有定义B。依赖链需要更精确:先定义A的接口,再定义B的接口,再定义B和A的实现。
module type Asig = sig
type t = { x : int }
type u = int
val b : t
end
module B = (struct
let a : Asig.t = { Asig.x = 1 }
end : sig
val a : Asig.t
end)
module A = (struct
type t = { x : int }
let b = B.a
end : Asig)
嗯,不。
File "d.ml", line 7, characters 12-18:
Error: Unbound type constructor Asig.t
你看,Asig 是一个签名。签名是模块的规范,仅此而已; Ocaml 中没有签名演算。您不能引用签名的字段。您只能引用模块的字段。写A.t时,指的是模块A的名为t的类型字段。
在 Ocaml 中,这种微妙之处很少出现。但是你试图戳中语言的一个角落,这就是潜伏在那里的东西。
那么当有两个编译单元时会发生什么?更接近的模型是将A 视为一个函子,它将模块B 作为参数。 B 所需的签名是接口文件b.mli 中描述的签名。类似地,B 是一个函数,它接受一个模块 A,其签名在 a.mli 中给出作为参数。哦,等等,它有点复杂:A 出现在B 的签名中,所以B 的接口实际上定义了一个函子,它接受一个A 并产生一个B,可以这么说.
module type Asig = sig
type t = { x : int }
type u = int
val b : t
end
module type Bsig = functor(A : Asig) -> sig
val a : A.t
end
module B = (functor(A : Asig) -> (struct
let a : A.t = { A.x = 1 }
end) : Bsig)
module A = functor(B : Bsig) -> (struct
type t = { x : int }
let b = B.a
end : Asig)
在这里,在定义A 时,我们遇到了一个问题:我们还没有A,可以作为参数传递给B。 (当然,不包括递归模块,但在这里我们试图看看为什么没有它们我们就无法生存。)
定义生成类型是一个副作用
基本的症结在于type t = {x : int} 是一个生成类型定义。如果这个片段在一个程序中出现两次,则定义了两种不同的类型。 (Ocaml采取措施,禁止你在同一个模块中定义两个同名的类型,除了顶层。)
事实上,正如我们在上面看到的,type t = {x : int}在模块实现中是一个生成类型定义。它的意思是“定义一个新类型,称为d,它是一个带有字段的记录类型......”。相同的语法可以出现在模块接口中,但它有不同的含义:它的意思是“模块定义了一个类型t,它是一个记录类型……”。
由于定义一个生成类型两次会创建两个不同的类型,A 定义的特定生成类型不能完全由模块 A 的规范(其签名)描述。因此使用这种生成类型的程序的任何部分实际上都在使用A 的实现,而不仅仅是它的规范。
当您开始着手时,定义生成类型是一种副作用。这种副作用发生在编译时或程序初始化时(这两者之间的区别仅在您开始查看函子时才会出现,我不会在这里做。)所以跟踪这种副作用何时发生很重要:它当模块 A 被定义(编译或加载)时发生。
因此,更具体地表达这一点:模块A中的类型定义type t = {x : int}被编译为“让t成为类型#1729,一种新类型,它是一个带有字段的记录类型……”。 (fresh 类型意味着与以前定义的任何类型都不同。)。 B 的定义将 a 定义为具有 #1729 类型。
由于模块B依赖于模块A,所以A必须在B之前加载。但是A的实现显然使用了B的实现。两者是相互递归的。 Ocaml 的错误信息有点混乱,但你确实超出了语言的界限。