混合泛型类型

4

我想在我的 myTest 中混合使用通用类型。

case class CC(i:Int, s:String, s2:String)

case class Value[V](value: V)

def myTest[F](values: (CCValue[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");}
           ^
3个回答

5

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

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

2
在您的函数中,不是协变性而是存在类型。
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是一个整数。

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

其中F是一个String类型的参数。它可以适用于所有三个例子,因为对于参数列表中的所有元素,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: (CCValue[F] forSome { type F } )*) = Unit
myTest: (values: CC => Value[_]*)Unit.type

添加的 forSome { type F } 表示存在一个 F,使得参数的类型为 CC => Value[F],但是“忘记”了具体的 F。因此,Value[Int] 变成了未知的 FValue[F]Value[String] 也是如此。从外部来看,它们都变成了相同的类型,所以可以正常工作。虽然这种语法非常清晰,但大多数情况下可以通过下划线缩短:
scala> def myTest(values: (CC ⇒ Value[_])*) = Unit
myTest: (values: CC => Value[_]*)Unit.type

1
重新定义myTest如下:
def myTest(values: CC => Value[_]*) = Unit

这样,传递给myTest的每个函数对象都将具有适当的类型参数,用于返回的Value
如果使用协变(例如Value[+V]),您可以将Value替换为Any,因为Scala编译器将在myTest[F]中提供Any作为推断的类型参数,这会导致失去很多类型检查。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接