抽象类型协方差/逆变

时间:2013-08-01 22:52:40

标签: functional-programming ocaml covariance contravariance typing

我正在使用此代码,我不明白。

type t1 = [ `A ];;
type t2 = [ t1 | `B ] ;;
type t3 = [ t2 | `C ] ;;

module type MT =
sig
  type ('a,'b) fct
  val create : ('a -> 'b) -> ('a,'b) fct
end;;

module Mt : MT =
struct
    type ('a,'b) fct = 'a -> 'b
    let create f = f
end;;

let f = (fun x -> x : t2 -> t2) ;;
let af = Mt.create (fun x -> x : t2 -> t2);;

(f :> t1 -> t3);;
(af :> (t1, t3) Mt.fct);;

像这样它不起作用,因为编译器不知道Mt.fct的类型参数是协变还是反变量。但是,如果您通过以下方式替换模块签名中的类型声明:

type (-'a,'+b) fct

告诉编译器b是协变的并且是逆变的,现在它可以工作了。而且因为我是一个棘手的小烦人,我试图通过告诉他一个也是协变的来欺骗编译器!

type (+'a,'+b) fct

他比我更聪明,他注意到我在骗他。

Type declarations do not match:
         type ('a, 'b) fct = 'a -> 'b
       is not included in
         type (+'a, +'b) fct
       Their variances do not agree.

我的问题是:如果他无论如何都知道类型参数的方差,为什么不将它用于我的模块,而不是强迫我添加那些+和 - 。这又是一个可判定性问题吗?

2 个答案:

答案 0 :(得分:3)

类型归属Mt:MT是不透明的。因此,编译器无法使用有关类型定义的信息,因为您可以随时更改定义(可以单独编译)。要看到这个,如果你这样做

module type MT =
sig
  type ('a,'b) fct = 'a -> 'b
  val create : ('a -> 'b) -> ('a,'b) fct
end;;

那么你编写的代码编译得很好。

答案 1 :(得分:0)

虽然编译器可以检查您的类型在特定参数位置中是否实际是协变或逆变,但它并不会立即假设这是您希望在抽象类型中公开的信息。毕竟,一旦编译器知道参数的方差,那么它将被允许根据需要对该参数执行强制 - 这可能根本不是您的公共API的一部分!