Types 为什么在这个类型之前有一个加号?

Types 为什么在这个类型之前有一个加号?,types,map,ocaml,Types,Map,Ocaml,我在浏览ocaml的标准库时,在map.ml文件中发现了这段代码 module type S = sig type key type +'a t val empty: 'a t' 我想知道为什么会有type+'at,为什么作者使用它而不是简单的'at 它的行为很奇怪,我无法推断它的用法 # type +'a t = 'a list;; type 'a t = 'a list # type +'a t = +'a list;; Characters 13-14:

我在浏览ocaml的标准库时,在map.ml文件中发现了这段代码

module type S =
  sig
    type key
    type +'a t
    val empty: 'a t'
我想知道为什么会有
type+'at
,为什么作者使用它而不是简单的
'at

它的行为很奇怪,我无法推断它的用法

# 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

谢谢

这将该类型标记为与模块类型相关的协变类型。假设您有两个键类型相同的贴图。该
+
表示,如果一个映射A的值属于另一个映射B的值的子类型,则映射A的整体类型是映射B类型的子类型。我在中找到了一个很好的描述。

以Jeffrey的答案为基础,开发人员在这里将抽象类型标记为协变类型的原因可能不会帮助您使用子类型(基本上没有人在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 ()
对于
测试
您得到的类型是
''u a C.collection
,而不是您期望的
'a C.collection
。它不是多态类型(
“\u a
是一个尚未完全确定的单态推理变量),在大多数情况下,您不会满意它

这是因为
C.empty()
不是一个值,所以它的类型不是泛化的(~made-polymorphic)。要从放松的值限制中获益,您必须将抽象类型标记为集合协变:

module type S = sig
  type +'a collection
  val empty : unit -> 'a collection
end
当然,这只是因为模块
C
是用签名
S
密封的:
module C:S=…
。如果模块
C
没有给出明确的签名,类型系统将推断出最普遍的方差(这里是协方差),人们不会注意到这一点

针对抽象接口编程通常是有用的(当定义函子、强制执行幻象类型规程或编写模块化程序时),因此这种情况肯定会发生,了解放松的值限制非常有用


如果你想理解这一理论,Jacques Garrigue在2004年的研究文章中讨论了价值限制及其放松,该文章的前几页是对主题和主要思想的非常有趣和容易理解的介绍。

谢谢你的详细解释。我回到map.ml,发现了下面的一行:输入'at=Empty |节点'at*key*'a*'a t*int,这与您的示例非常相似。然而,你能解释更多关于单态推理类型吗?似乎“C.collection仍然匹配”C.collection。如果C.empty()的类型不是泛化的,那么它什么时候会导致什么问题?@octref:尝试使用函数
let id=(fun x->x)(fun x->x)
,它的类型是
“\u a->”\u a
(因为它的定义是一个应用程序而不是一个值,并且它的类型变量出现在正位置和负位置)。你很快就会发现问题:你不能在两种不同的类型上使用它,所以它不是多态的。谢谢gasche,了解它真的很好。我想象它只是关于普通的旧的子类型(现在你提到了,我几乎从未使用过)。谢谢gasche,现在我明白了。如果map A是map B类型的子类型,那么这是否意味着我可以在A上使用B的方法?它与Java中的子类概念相似吗?OCaml的非OO部分与Java根本不相似。Java子类化是一种子类型化,但是(我相信这里的gasche)在OCaml中很少使用子类型化,而子类化是Java的一个关键特性。请注意,OCaml不推断子类型。您需要显式地将表达式的类型强制为超类型。这很好,因为它几乎从未出现过。但是,当您这样做时,那么是的,它允许为超类型定义的函数(不仅仅是方法,所有函数)应用于子类型。感谢您的解释!简街的相关帖子: