我正在尝试使用OCaml(3.12.1)的模块语言,为模块定义函子和签名,等等,主要是按照Chapter 2 of the OCaml manual的示例进行,并且偶然发现了我的心理模型,函数和模块签名的工作方式存在缺陷。我试图将遇到的情况缩小到尽可能短的代码,所以不要问我要完成什么,这是一个完全人为设计的示例,用于演示有问题的OCaml功能。

因此,我们有一个函子,该函子仅提供标识函数“ f”,并由提供该函数输入参数类型的模块进行参数化。像我说的那样完全是人为的例子。

module type SOMETYPE = sig type t end ;;
module Identity = functor (Type: SOMETYPE) -> struct let f (x: Type.t) = x end ;;


鉴于以上所述,我们继续定义一个提供int类型的模块:

module IntType = struct type t = int end ;;


..然后我们使用函子为int身份函数生成一个模块:

module IdentityInt = Identity(IntType) ;;


确保生成的模块及其f函数的行为符合预期:

#IdentityInt.f(3) + 10 ;;
- : int = 13


函子的心理模型是将模块作为输入模块和返回模块的函数,到目前为止似乎在为我们服务。 Identity函子期望将签名(模块类型)SOMETYPE的模块作为输入参数,并且确实我们提供的模块(IntType)具有正确的签名,因此将生成一个有效的输出模块(IdentityInt),其函数的行为符合预期。

现在是不直观的部分。如果我们想让提供的模块f确实是SOMETYPE类型的模块,该怎么办。如:

module IntType : SOMETYPE = struct type t = int end ;;


然后以与以前相同的方式生成函子的输出模块:

module IdentityInt = Identity(IntType) ;;


...让我们尝试使用新生成的模块的IntType函数:

IdentityInt.f 0 ;;


因此,REPL投诉:

"Error: This expression [the value 0] has type int but an expression was expected of type IntType.t."


提供冗余但正确的类型信息如何破坏代码?即使在A的情况下,函子模块Identity也必须将f模块视为IntType类型。那么,如何显式地将SOMETYPE声明为IntType类型会产生不同的结果呢?

最佳答案

:构造在核心语言和模块语言方面有所不同。在核心语言中,它是一个注释构造。例如,((3, x) : 'a * 'a list)约束表达式具有某种类型,该类型是'a * 'a list的实例;由于该对的第一个元素是整数,因此let (a, b) = ((3, x) : 'a * 'a list) in a + 1的类型正确。模块上的:构造并不意味着这个。

构造M : S将模块M密封到签名S。这是不透明的印章:键入S时,只有签名M : S中给出的信息仍然可用。当您编写module IntType : SOMETYPE = struct type t end时,这是替代语法

module IntType = (struct type t end : SOMETYPE)


由于未指定t中的类型字段SOMETYPE,因此IntType具有抽象类型字段t:类型IntType是由该定义生成的新类型。

顺便说一句,您可能是说module IntType = (struct type t = int end : SOMETYPE);但无论哪种方式,IntType.t都是抽象类型。

如果要指定某个模块具有某些特征,同时又暴露某些类型,则需要为这些类型添加显式相等性。没有添加所有可推断的相等性的构造,因为对模块应用签名通常意味着信息隐藏。为了简单起见,该语言仅提供了这一生成密封结构。如果要使用定义的签名SOMETYPE并保留类型t的透明度,请向签名添加约束:

module IntType = (struct type t = int end : SOMETYPE with type t = int)

10-04 12:27