Scala - 映射到函数,反对逆向变换

时间:2015-05-01 12:48:24

标签: scala dictionary covariance contravariance

假设我有

trait A
case class S(s:String) extends A
case class B(b:Boolean) extends A

val m = scala.collection.mutable.HashMap[String,(Seq[C]) => Option[A]](
    "testS" -> ((cs:Seq[C]) => Some(S(foo(cs)))),
    "testB" -> ((cs:Seq[C]) => Some(B(bar(cs)))),
    ...
)

现在假设我们的D类型为D <: C

val m = scala.collection.mutable.HashMap[String,(Seq[C]) => Option[A]](
    "testS" -> ((cs:Seq[C]) => Some(S(foo(cs)))),
    "testB" -> ((cs:Seq[C]) => Some(B(bar(cs)))),
    "testD" -> ((ds:Seq[D]) => Some(B(baz(ds.head)))), //oops!
    ...
)

是的,像我一样愚蠢,我再次忘记了论据应该是逆变的,这意味着

D <: C, therefore (C => E) <: (D => E)

当然,Scala不会让我这样做:“类型不匹配”

使用地图的整个想法是客户端应该能够添加自己的映射。当然,我可以简单地要求添加这样的案例,如

    "testD" -> ((ds:Seq[C]) => Some(B(baz(ds.head.asInstanceOf[D]))))

但这是唯一的解决方案吗?

1 个答案:

答案 0 :(得分:0)

在这里使用asInstanceOf很危险。如果ds.head实际上是其他D2 D2 <: C的实例,该怎么办?这不是一个逆变问题,因为你需要优化你的抽象,以便类型更有意义。

您应该考虑如何使用字典m - 您是否尝试将其所有值应用于某些通用Seq[C]?如果是这样,则不应允许客户端传递D => E类型的函数,因为这些函数可能不适用于C的所有实例。如果每个客户端都有自己的字典,其中所有方法都有一个类型的参数,那么客户端实例化在某些类型X <: C中应该是通用的,然后m可以包含{{1}的方法}}。

当然,需要更多详细信息来提出一个可能在这里工作的类型系统,因为现在你的函数列表的唯一自然类型是X => E,这是因为它自己的原因是危险的。以下是您的问题的简化版本,可能有助于阐明该问题:

Any => E