2017-08-08 64 views
0

考虑这个简单的例子:协变型FParam发生在逆变位置类型的值序号[FParam]猜测

trait Optimizer[+FParam, FRes] { 
    def optimize(
    fn: (FParam) => FRes, 
    guesses: Seq[FParam] // <--- error 
) 
} 

它不编译,因为

协变型FParam在逆变发生在价值猜测Seq[FParam]类型的位置。

但是seq被定义为trait Seq[+A],那么这个反变量的来源是什么? (问题1

相反地,考虑这个简单的例子与-FParam:在协变位置发生

trait Optimizer[-FParam, FRes] { 
    def optimize(
    fn: (FParam) => FRes, // <--- error 
    guesses: Seq[FParam] 
) 
} 

反变型在型(FParam) => FRes

再次,相同的矛盾:在Function1[-T1, R],第一个类型参数显然是逆变的,那么为什么FParam处于协变位置? (Question2

我可以通过翻转Lower type bounds中描述的方差来解决这个问题,但为什么它有必要不清楚。

trait Optimizer[+FParam, FRes] { 
    type U <: FParam 

    def optimize(
    fn: (FParam) => FRes, 
    guesses: Seq[U] 
) 
} 

回答

0

问题是FParam没有直接使用。它的参数是optimize,因此其方差翻转。为了说明这一点,让我们来看看optimize类型(见val optim):

trait Optimizer[+FParam, FRes] { 
    type U <: FParam 

    def optimize(
    fn: (FParam) => FRes, 
    guesses: Seq[U] 
) 

    val optim: Function2[ 
    Function1[ 
     FParam, 
     FRes 
    ], 
    Seq[U], 
    Unit 
    ] = optimize 
} 
2

问题1+FParam意味着FParam它从不同亚型FParam。对于guesses它期望cotravariant typeSeq。所以,你可以通过明确说明FPParam本的supertype,像这样做:

def optimize[B >: FParam](fn: (B) => FRes, guesses: Seq[B]) // B is the super type of FParam 

或类似的SeqViewLike

为什么guesses它期望cotravariant typeSeq?例如:

trait Animal 

case class Dog() extends Animal 

case class Cat() extends Animal 
val o = new Optimizer2[Dog, Any] 
val f: Dog => Any = (d: Dog) => ... 
o.optimize(f, List(Dog(), Cat())) // if we don't bind B in `guesses` for supertype of `FParam`, it will fail in compile time, since the `guesses` it's expecting a `Dog` type, not the `Animal` type. when we bind it to the supertype of `Dog`, the compiler will infer it to `Animal` type, so `cotravariant type` for `Animal`, the `Cat` type is also matched. 

问题2-FParamcotravairant类型FParam和到它的亚型。对于fn: Function1[-T1, +R]//(FParam) => FRes它期待协变类型此它从超类型FParam变化。所以你可以通过问题1类型的状态,如:

def optimize[B <: FParam](fn: (B) => FRes, guesses: Seq[B]) // B is the sub type of FParam