Scala-映射到函数,与逆变作斗争



假设我有

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

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

您在这里使用asInstanceOf是危险的。如果ds.head实际上是其他D2 <: CD2的实例呢?这不是一个相反的问题,而是你需要完善你的抽象,使类型更有意义。

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

当然,提出一个可能在这里工作的类型系统需要更多的细节,因为现在函数列表中唯一的自然类型是Any => E,这本身就很危险。以下是您的问题的简化版本,可能有助于阐明问题:

import scala.collection.mutable._
trait C
trait D extends C
val cfunc: C => Int = c => 1
val dfunc: D => Int = d => 1
var list = ListBuffer[C => Int](cfunc)
list += dfunc

相关内容

  • 没有找到相关文章

最新更新