考虑这个简单的例子:
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]
)
}
答案 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))
}
特征(现在是一个抽象类)仍然具有协变泛型参数,但是在其参数中使用该协变泛型的函数被类型锁定为具有不变量的精确类型 - 现在必须在函数中使用相同的类或子类单例对象。