带有通配符类型参数的map上的flatMap

时间:2012-02-10 18:49:28

标签: generics scala types scala-collections

我正试图写这样的东西:

trait Typed[T]

trait Test {

  def testMap: Map[Typed[_], Int]

  def test = testMap.flatMap {case (typed, size) => Seq.fill(size)(typed)}
}

但是我收到以下错误:

error: no type parameters for method flatMap: (f: ((Typed[_], Int)) => Traversable[B])(implicit bf: scala.collection.generic.CanBuildFrom[scala.collection.immutable.Map[com.quarta.service.querybuilder.Typed[_],Int],B,That])That exist so that it can be applied to arguments (((Typed[_], Int)) => Seq[Typed[_0]] forSome { type _0 })
--- because ---
argument expression's type is not compatible with formal parameter type;
found   : ((Typed[_], Int)) => Seq[Typed[_0]] forSome { type _0 }
required: ((Typed[_], Int)) => Traversable[?B]
def test = testMap.flatMap {case (typed, size) => Seq.fill(size)(typed)}

如果将testMap类型更改为:

,则此代码有效
def testMap: Map[Typed[Any], Int]

有什么区别以及如何解决我的问题?

3 个答案:

答案 0 :(得分:5)

如果我正确理解了您的问题,答案是:如果Typed中的T是协变的,即trait Typed[+T],则可以执行此操作。

实施例

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Typed[+T: Manifest] {
  override def toString = "Typed[" + implicitly[Manifest[T]].toString + "]"
}

trait Test {
  def testMap: Map[Typed[_], Int]

  def foo = testMap flatMap { case (t, s) => Seq.fill(s)(t) }
}

val bar = new Test { 
  def testMap = Map(new Typed[Double]() -> 3, new Typed[Int]() -> 5)
}

// Hit Ctrl-D

scala> bar.foo
res0: scala.collection.immutable.Iterable[Seq[Typed[Any]]] = List(Typed[Double], Typed[Double], Typed[Double], Typed[Int], Typed[Int], Typed[Int], Typed[Int], Typed[Int])

请注意,我在此示例中为Typed创建了一个类,以获得更好的输出。你当然可以坚持使用trait

现在,为什么需要协方差?

协方差基本上意味着如果A <: B那么X[A] <: X[B]。因此,如果您将testMap声明为Map[Typed[Any], Int]Typed 不变,则不允许您传递,例如即使Typed[Double]Typed[Any]的{​​{1}}也是Double <: Any。在这里,scala编译器似乎在协变情况下用_替换Any(请参阅extempore的注释以详细阐述)。

有关下划线问题的解释,我会参考Luigi的答案。

答案 1 :(得分:4)

我认为问题是你试图将匿名函数与存在类型参数进行模式匹配。

从语言规范,第8.5节关于模式匹配匿名函数:

  

必须部分定义此类表达式的预期类型。它   某些scala.Functionk[S1, ... , Sk, R]必须为k > 0,或者   scala.PartialFunction[S1, R]参数type(s) S1, ... , Sk   必须完全确定,但结果类型R可能是   未确定。

testMap是一种存在类型(参见语言规范3.2.10)。存在类型的格式为T forSome {Q},其中Q是类型声明的序列。您使用了特殊占位符语法,因此类型Map[Typed[_], Int]等同于Map[Typed[t] forSome { type t }, Int],这可能会使错误消息更有意义。

至于解决方案,我想这完全取决于你想要做什么,你不会说......:)

答案 2 :(得分:0)

参数化测试不是一个选项吗?

trait Test [A] {
   def testMap: Map [Typed [A], Int]
   def test = testMap.flatMap {case (typed, size) => Seq.fill (size)(typed)}
}

在知道A将会是什么之前,您是否必须申报测试?