多态变体和类型签名

Polymorphic variants and type signatures

(这是 的扩展/提炼)

考虑以下代码:

版本 1:

let x : [> `Error1 ] = (`Error1 : [> `Error1 ])
let y : [> `Error1 | `Error2 ] = x

版本 2:

let x : [> `Error1 ] = (`Error1 : [ `Error1 ])
let y : [> `Error1 | `Error2 ] = x

版本 1 类型检查,但版本 2 失败(我正在使用 4.09.0 进行编译):

File "test.ml", line 2, characters 33-34:
2 | let y : [> `Error1 | `Error2 ] = x
                                     ^
Error: This expression has type [ `Error1 ]
       but an expression was expected of type [> `Error1 | `Error2 ]
       The first variant type does not allow tag(s) `Error2

注意这个错误出现在y的定义中,但是x的签名在这两种情况下是一样的! y怎么能看到x的定义里面呢?关于 x 的类型检查信息是否比其签名更多?

简而言之,显式类型注释不是类型签名。 特别是在

let x : [> `Error1 ] = (`Error1 : [ `Error1 ])

x 的类型是 [ `Error1 ]

问题的根源是显式类型注释中的统一类型变量可以与具体类型统一。

你的问题的一个更简单的例子是

let f: 'a -> 'a = fun x -> x + 1

此处,'a -> 'a 注释与真实类型 int->int 统一,因此此代码进行了类型检查。如果要使类型变量 'a 通用,则需要通过添加显式通用量化

来更加明确
let f: 'a. 'a -> 'a = fun x -> x + 1
Error: This definition has type int -> int which is less general than
'a. 'a -> 'a

使用隐式行变量的代码也会出现同样的现象:

let x : [> `Error1 ] = (`Error1 : [ `Error1 ])

这个注解不保证x的类型是[> `Error1],只保证可以和[> `Error1]统一。并且由于类型 [ `Error1 ] 可以与 [> `Error1 ] 统一,因此没有理由引发错误。

和以前一样,如果你想避免这个问题,你需要在注释中明确说明哪些变量被普遍量化:

let x : 'row. ([> `Error1 ] as 'row) = (`Error1 : [ `Error1 ])
Error: This expression has type [ `Error1 ]
      but an expression was expected of type [> `Error1 ]
      The second variant type is bound to the universal type variable 'a,
      it cannot be closed