我正在浏览ocaml的标准库,并在map.ml文件中遇到了这段代码。
module type S =
sig
type key
type +'a t
val empty: 'a t'
我想知道为什么有type +'a t
,为什么作者使用它而不仅仅是'a t
。
它的行为很奇怪,我不能推断出它的用法。
# type +'a t = 'a list;;
type 'a t = 'a list
# type +'a t = +'a list;;
Characters 13-14:
type +'a t = +'a list;;
^
Error: Syntax error
由于
答案 0 :(得分:15)
为了建立Jeffrey的答案,开发人员将抽象类型标记为协变的工作原因可能是而不是来帮助您使用子类型(基本上没有人在OCaml中使用子类型,因为参数化多态性通常是首选的),但要使用称为“宽松值限制”的类型系统的一个更为人熟知的方面,这要归功于协变性抽象类型允许更多的多态性。
你可以放心地忽略那些细微之处,直到有一天你遇到一个你自己的抽象类型的问题,而不是你想要的多态,然后你应该记住,签名中的协方差注释可能会有所帮助。
几个月前我们讨论过on reddit/ocaml:
请考虑以下代码示例:
module type S = sig type 'a collection val empty : unit -> 'a collection end module C : S = struct type 'a collection = | Nil | Cons of 'a * 'a collection let empty () = Nil end let test = C.empty ()
test
获得的类型为'_a C.collection
,而不是您期望的'a C.collection
。它不是多态类型('_a
是一个尚未完全确定的单态推理变量),在大多数情况下你不会满意。这是因为
C.empty ()
不是一个值,所以它的类型不是一般化的(〜多态)。要从宽松的值限制中受益,您必须标记抽象类型'a collection
协变:module type S = sig type +'a collection val empty : unit -> 'a collection end
当然这只会发生,因为模块
C
已使用签名S
密封:module C : S = ...
。如果模块C
没有给出明确的签名,那么类型系统会推断出最一般的方差(这里是协方差),并且人们不会注意到这一点。针对抽象接口进行编程通常很有用(当定义仿函数,或强制执行幻像类型规则或编写模块化程序时),因此这种情况肯定会发生,因此了解宽松值限制是有用的。 / p>
如果你想理解这个理论,那么价值限制及其放松将在Jacques Garrigue的2004年研究文章Relaxing the value restriction中讨论,他的前几页是一个相当有趣且易于理解的主题和主要思想的介绍
答案 1 :(得分:12)
这将类型标记为与模块类型相关的协变量。假设您有两个键,它们的键类型相同。这个+
表示如果一个地图A的值是另一个地图B的值的子类型,则地图A的整体类型是地图B的类型的子类型。我在Jane Street blog中找到了很好的描述。