2016-10-28 76 views
2
abstract class Bhanu[-A] { val m:List[A] } 

逆變和協方差在斯卡拉

error: contravariant type A occurs in covariant position in type => List[A] of value m 
     abstract class Bhanu[-A] { val m:List[A] } 

abstract class Bhanu[+A] { val m:List[A] } 

defined class Bhanu 

我不能換我的頭周圍這個概念,爲什麼它違反協議失敗,而協議成功方差。

其次(從其他例子),

聲明究竟意味着什麼?

Function1[Sport,Int] <: Function1[Tennis,Int] since Tennis <: Sport 

這對我來說似乎是違反直覺的,不應該是以下情況嗎?

Function1[Tennis,Int] <: Function1[Sport,Int] since Tennis <: Sport 

回答

5

讓我們來看看你提到的第一個例子。 考慮,我們有:

class Fruit 
class Apple extends Fruit 
class Banana extends Fruit 

class Bhanu[-A](val m: List[A]) // abstract removed for simplicity 

由於Bhanu是contravatiant Bhanu[Fruit] <: Bhanu[Apple]這樣你就可以做到以下幾點:

val listOfApples = new List[Apple](...) 
val listOfFruits = listOfApples // Since Lists are covariant in Scala 
val a: Bhanu[Fruit] = new Bhanu[Fruit](listOfFruits) 
val b: Bhanu[Banana] = a // Since we assume Bhanu is contravariant 
val listOfBananas: List[Banana] = b.m 
val banana: Banana = listOfBananas(0) // TYPE ERROR! Here object of type Banana is initialized 
             // with object of type Apple 

所以Scala編譯器的限制,保護我們免受這種錯誤的協變位置使用逆變類型參數。

對於第二個問題,我們來看一下這個例子。考慮到我們擁有的功能:

val func1: Function1[Tennis,Int] = ... 

如果Function1[Tennis,Int] <: Function1[Sport,Int]其中Tennis <: Sport你提出比我們能做到以下幾點:

val func2: Function1[Sport,Int] = func1 
val result: Int = func2(new Swimming(...)) // TYPE ERROR! Since func1 has argument 
              // of type Tennis. 

但是,如果我們做Function1逆變其參數,所以Function1[Sport,Int] <: Function1[Tennis,Int]其中Tennis <: Sport比:

val func1: Function1[Tennis,Int] = ... 
val func2: Function1[Sport,Int] = func1 // COMPILE TIME ERROR! 

對於相反的情況一切都很好:

val func1: Function1[Sport,Int] = ... 
val func2: Function1[Tennis,Int] = func1 // OK! 
val result1: Int = func1(new Tennis(...)) // OK! 
val result2: Int = func2(new Tennis(...)) // OK! 

功能必須在其參數類型和協變進行逆變在結果類型:

trait Function1[-T, +U] { 
    def apply(x: T): U 
} 
+0

我認爲你的意思是蘋果延伸Fruit而不是Base。 –

+0

@Yuval修復,謝謝。 – dkolmakov

4

dkolmakov's answer做得很好解釋爲什麼這個特殊的例子是行不通的。也許更一般的解釋也會有幫助。

對於類型構造函數,函數或特徵是否有差異,這意味着什麼?按照definition on Wikipedia

在一種編程語言,打字規則或 類型構造的類型系統是:

  • 協變:如果保留的類型 排序(≤ ),它定義了從更具體到更通用的類型;

  • 逆變:如果它逆轉這個順序;

  • 如果兩者都不適用,則爲不變或非變異。

現在,什麼是對的類型進行排序?而這個世界是什麼意思來保存或逆轉排序呢?這意味着,對於任何類型的TU,有要麼存在的關係,其中:

  • 協方差:T <: U - >M[T] <: M[U] - 例如,Cat <: Animal,所以List[Cat] <: List[Animal]
  • 逆變:T <: U - >M[T] >: M[U] - 例如,一個Cat <: Animal,所以Function1[Cat, Unit] >: Function1[Animal, Unit]

或不變,如果兩者之間沒有關係。

請注意協方差保留類型之間的順序,因爲CatAnimal派生。現在注意逆變顛倒了順序,因爲現在Function0[Animal, Unit]派生出Function0[Cat, Unit]

我們怎樣才能把這個變化的概念帶到我們的優勢呢?根據這些規則,我們可以概括類型構造函數之間的賦值兼容性!很好的例子是List[A],Option[A]Function1[-T, +U](或真的任何FunctionN)。

我們以Function1[-T, +U]T => U)爲例,它有一個協變和逆變參數。

爲什麼輸入類型參數是逆變量,輸出類型是協變?首先,根據上述定義的公理,我們可以看到:

Function1[Sport,Int] <: Function1[Tennis,Int] 

輸入類型參數反轉上類型的關係的,因爲通常,Tennis <: Sport,但在這裏它是相反。這是爲什麼? 由於Sport中的任何函數都知道如何處理Tennis,但事實恰恰相反。例如:

val sportFunc: (Sport => Int) = ??? 
val tennisFunc: (Tennis => Int) = sportFunc 

val result = tennisFunc(new Tennis()) 

不過,如果一個函數期待一個Tennis知道如何處理任何Sport?當然不是:

val tennisFunc: (Tennis => Int) = ??? 
val sportFunc: (Sport => Int) = tennisFunc 

// The underlying function needs to deal with a Tennis, not a `FootBall`. 
val result = sportFunc(new FootBall()) 

相反的關於這是協變的輸出類型是真實的,任何人都期待Sport返回類型可以處理Tennis,或FootBallVollyBall