【问题标题】:Covariant type FParam occurs in contravariant position in type Seq[FParam] of value guesses协变类型 FParam 出现在值猜测类型 Seq[FParam] 的逆变位置
【发布时间】:2018-01-16 03:40:24
【问题描述】:

考虑这个简单的例子:

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) =&gt; FRes类型的协变位置

同样的悖论:在Function1[-T1, R] 中,第一个类型参数显然是逆变的,那么为什么FParam 处于协变位置? (问题2

我可以按照Lower type bounds 中的描述翻转方差来解决此问题,但为什么需要这样做尚不清楚。

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

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

【问题讨论】:

    标签: scala covariance contravariance scala-generics


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

      问题 1+FParam 表示 协变 类型 FParam,它从 超类型 到子类型 FParam 不等。对于guesses,它期待cotravariant type 对于Seq。因此,您可以为此明确声明 supertypeFPParam 来做到这一点,例如:

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

      或喜欢SeqViewLike

      那么为什么 guesses 它期待 cotravariant type 用于 Seq?例如:

      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) =&gt; FRes 它需要 covariant 类型。因此,您可以通过反转 Question 1 类型状态来做到这一点,例如:

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

      【讨论】:

        【解决方案3】:

        问题是没有直接使用 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
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-11-29
          • 1970-01-01
          • 2020-01-25
          • 1970-01-01
          相关资源
          最近更新 更多