协变型FParam出现在价值猜测的Seq [FParam]类型的逆变位置

时间:2017-08-08 14:04:04

标签: scala covariance contravariance scala-generics

考虑这个简单的例子:

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处于协变位置? (的问题2

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

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

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

3 个答案:

答案 0 :(得分:2)

问题1 +FParam表示协变类型FParam,它从超类型到子类型{{ 1}}。 FParam guesses期待cotravariant type的{​​{1}}。因此,您可以通过明确声明Seq supertype来实现此目的,例如:

FPParam

或喜欢SeqViewLike

那么为什么def optimize[B >: FParam](fn: (B) => FRes, guesses: Seq[B]) // B is the super type of FParam 期待guesses的{​​{1}}呢?例如:

cotravariant type

问题2 Seq表示 cotravairant 类型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. ,它从超类型-FParam子类型不等 .for FParam它期待协变类型。因此,您可以通过反转问题1 类型状态来执行此操作,例如:

FParam

答案 1 :(得分: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 :(得分:0)

有了 just worked through this problem,希望我能解决这个困惑。

Seq[] 不是列表,并且其中不能同时包含一种以上的类类型。您的 trait Optimizer 有一个未知但固定的类 FParam 或一个子类,但您的函数返回一个 Seq[FParam] 不一定是与封闭对象相同的子类。因此,运行时可能会同时在 Seq[FParam] 中结束不同的子类,因此整个代码块会变成 Scala 编译错误。

abstract class Optimizer[+FParam, FRes] {

}

object Optimizer{
   def optimize[FParam,FRes](obj: Optimizer[FParam,FRes], param : FParam) : (FParam,Seq[FParam]) = (param,(Nil)) 
}

特征(现在是一个抽象类)仍然具有协变泛型参数,但是在其参数中使用该协变泛型的函数被类型锁定为具有不变量的精确类型 - 现在必须在函数中使用相同的类或子类单例对象。