2017-02-11 7 views
3

Простой пример для иллюстрации вопроса:Почему scala не выводит вывод типа для f-связанного полиморфизма?

trait WTF[W <: WTF[W]] { 
    def get : Int 
} 

trait Zero extends WTF[Zero] { 
    override def get : Int = 0 
} 
case object Zero extends Zero 

final case class Box(inner : Int) extends WTF[Box] { 
    override def get : Int = inner 
} 

def printWTF[W <: WTF[W]](w : W) = println(w.get) 

printWTF(Box(-1)) 
printWTF(Zero) 

Box это нормально, но Zero производит ошибку:

WTF.scala:22: error: inferred type arguments [Zero.type] do not conform to method printWTF's type parameter bounds [W <: WTF[W]] 
    printWTF(Zero) 
^
WTF.scala:22: error: type mismatch; 
found : Zero.type 
required: W 
    printWTF(Zero) 
     ^
two errors found 

Если я аннотирования тип вручную, он компилирует:

printWTF[Zero](Zero) 
printWTF(Zero : Zero) 

Первый линия работает, как ожидалось. Я часто сталкиваюсь с случаями, когда параметры типа не могут быть выведены из аргументов. например def test[A](x : Int) : Unit. Тип A нигде не встречается в сигнатуре аргумента, поэтому вы должны указать его вручную.

Но последнее очень мало для меня. Я просто добавил тип приведения, который всегда является истинным, и чудом компилятор узнает, как вывести параметры типа метода. Но Zero всегда из Zero типа, почему компилятор не мог вывести его без подсказок от меня?

ответ

2

Объект объекта Zero имеет тип Zero.type и является подтипом WTF[Zero]. Поэтому, когда вы вызываете printWTF(Zero), компилятор сообщает W = Zero.type, но Zero.type <: WTF[Zero.type] является ложным, поэтому компиляция не выполняется.

Это более сложная подпись, с другой стороны, должны работать:

def printWTF[W <: WTF[W], V <: W](w: V with WTF[W]) = println(w.get) 

И как демонстрация того, что это действительно выводит типы правильно:

scala> def printWTF[W <: WTF[W], V <: W](w: V with WTF[W]): (V, W) = ??? 
printWTF: [W <: WTF[W], V <: W](w: V with WTF[W])(V, W) 

scala> :type printWTF(Box(1)) 
(Box, Box) 

scala> :type printWTF(Zero) 
(Zero.type, Zero) 
+0

> объект Case Нулевой имеет тип Zero.type и является подтипом WTF [Zero] Но общее правило заключается в том, что вы можете заменить любую запись класса любым подклассом. Пример второй строки делает то же самое. Он не меняет подписи типов. – ayvango

+0

Я не думаю, что кто-то знает полные правила вывода типа в Scala. Все, что я знаю, это то, что он не повторяет для всех супер типов «Zero.type» в вашем примере и что вам нужно немного помочь с некоторыми дополнительными параметрами, как я. И если вы имели в виду, что вы можете заменить «Zero» в 'WTF [Zero]' на 'Zero.type', вы ошибаетесь, потому что' W' в 'WTF' является инвариантным. –

+0

Я не имел в виду, что 'Zero' внутри' WTF [Zero] 'может быть заменен. Но где угодно, где ожидаемый аргумент «Zero» он может быть заменен на «Zero.type», потому что вызов функции разрешен, 'Function [Zero, T]' является подтипом функции [Zero.type, T] ' – ayvango