1

我正在玩這個代碼,我不明白的東西。抽象類型convariance/contravariance

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. 

我的問題是:如果他反正知道類型參數的變化,爲什麼不只是使用我的模塊,而不是迫使我添加這些+和 - 。這又是一個可判定性問題嗎?

+0

'A',B'是什麼? –

+0

這就是你如何定義多態變體。這幾乎就像做類型t = A | B,除了你可以用A和B創建其他類型,從而創建子類型。 –

回答

3

類型歸屬Mt:MT是不透明的。因此,編譯器無法使用有關類型定義的信息,因爲您可以隨時更改定義(可以單獨編譯)。要看到這一點,如果你這樣做

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

然後你寫的代碼編譯罰款。

+0

那麼我明白,如果你用這種簽名寫一個函子,他就不知道。但是在這裏我有一個實現,他可以知道我的類型有什麼變化。 –

+1

問題是它確實沒有實現。例如,您可以將實現放在另一個文件中,單獨編譯,然後編譯器只會讓接口繼續。你不希望一個程序在被拆分成不同的文件時編譯失敗,而不是當它在同一個文件中時編譯失敗。編譯器在(不透明)簽名歸屬期間確實隱藏了實現。 – seanmcl

0

儘管編譯器可以執行檢查,以確定您的類型在特定參數位置中是實際協變還是逆變,但它並不會立即假定這是您希望在抽象類型中公開的信息。畢竟,一旦編譯器意識到參數的變化,那麼它將被允許根據需要對該參數執行強制操作 - 這可能不會成爲公共API的一部分!