混合通用类型

Mixing generic types

我想在我的 myTest

中混合泛型类型
case class CC(i:Int, s:String, s2:String)

case class Value[V](value: V)

def myTest[F](values: (CC ⇒ Value[F])*) = Unit

class QF[M](x: M) {
  def eqs(v: M) = Value(x)
}

implicit def conversion[V](v: V): QF[V] = new QF(v)

myTest(_.i eqs 1)
myTest(_.s eqs "lol")
myTest(_.s eqs "lol", _.s2 eqs "lol2")

在那之前它一直有效,但我认为当我尝试混合类型时出现协方差错误。

当我这样做时:

myTest(_.i eqs 1, _.s eqs "lol")

我收到以下错误:

Error:(16, 13) type mismatch;
 found   : A$A135.this.CC => A$A135.this.Value[Int]
 required: A$A135.this.CC => A$A135.this.Value[Any]
myTest(_.i eqs 1, _.s eqs "lol");}
           ^

是的,没错。因为ValueV中被定义为不变的,这意味着Value[Int]Value[String]不是Value[Any]的子类型,但是AnyIntString 的最接近的公共超类型,即 myTest 中的 F。因为V在协变的位置,所以可以简单修改为:

case class Value[+V](value: V)

不是协方差而是存在类型。在你的函数中

def myTest[F](values: (CC ⇒ Value[F])*) = Unit

F 可以随对 myTest 的调用而变化,但在参数列表 (CC ⇒ Value[F])* 中是固定的。你的例子中的参数类型是

scala> val i_eqs = (cc : CC) => cc.i eqs 1
i_eqs: CC => Value[Int] = <function1>

其中 F 就是 Int.

scala> val s_eqs = (cc : CC) => cc.s eqs "lol"
s_eqs: CC => Value[String] = <function1>

其中 FString。它适用于所有 3 个示例,因为 F 对于参数列表中的所有元素都是相同的。但是当你打电话

myTest(_.i eqs 1, _.s eqs "lol")

编译器试图将 CC => Value[Int] (第一个参数的类型)统一为 CC => Value[String] (第二个参数),这减少了将 Value[Int]Value[String] 统一。实际上,您可以将 Value 定义为协变的,这可能是个好主意。但我想你在这里真正想要的是表达 myTest 的参数是“CC => Value[F] 的某种类型 F 的列表,它可能与列表中的另一个参数不同. 究竟哪些是存在类型 :) 长版本可能更清楚:

scala> def myTest(values: (CC ⇒ Value[F] forSome { type F } )*) = Unit
myTest: (values: CC => Value[_]*)Unit.type

添加的 forSome { type F } 声明存在一个 F,其参数类型为 CC => Value[F],但 "forget" 关于 F。因此 Value[Int] 变为 Value[F] 未知 F 并且 Value[String] 也是如此。从外面看,它们都变成了同一类型,所以它起作用了。这种语法虽然非常清晰,但大多数时候可以通过下划线缩短:

scala> def myTest(values: (CC ⇒ Value[_])*) = Unit
myTest: (values: CC => Value[_]*)Unit.type

像这样重新定义 myTest

def myTest(values: CC => Value[_]*) = Unit

这样,传递给 myTest 的每个函数对象都会有一个适合 Value 的类型参数,它 returns.

如果您使用协方差(例如 Value[+V]),您不妨将 Value 替换为 Any,因为 Scala 编译器将提供 Any 作为推断类型对于 myTest[F] 中的 F,您将失去很多类型检查。