0

考虑这个简单的例子:

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

它不会编译,因为

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

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

相反,考虑这个简单的例子-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]
  )
}
4

3 回答 3

2

问题1+FParam表示协变类型FParam,它因类型而异FParam。因为它guesses期待cotravariant type. Seq因此,您可以通过为此明确声明 a supertypeof来做到这FPParam一点,例如:

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-FParam表示cotravirant类型FParam,它从超类型FParam到它的类型而有所不同。因为它为此fn: Function1[-T1, +R]//(FParam) => FRes期待协变类型。因此,您可以通过反转问题 1类型状态来做到这一点,例如:

def optimize[B <: FParam](fn: (B) => FRes, guesses: Seq[B]) // B is the sub type of FParam
于 2017-08-08T15:43:01.987 回答
0

刚刚解决了这个问题,希望我能消除困惑。

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)) 
}

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

于 2021-05-02T20:09:08.553 回答
0

The problem is that FParam isn't used directly. It is in the argument of optimize, and as such its variance is flipped. To illustrate, let's look at the type of optimize (see 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
}
于 2017-08-08T14:37:13.627 回答