如何为自定义Scala集合实现newBuilder(具有正确的方差)?

时间:2015-03-09 16:32:32

标签: scala generics collections types builder

我正在尝试实现一个遵循相同习语的新集合类型 作为标准库,但我很难搞清楚如何处理 Builder机制。我读过了优秀的"Architecture of Scala Collections" doc page, 但它不包括我的情况。

以下是我正在尝试做的简化版本:

import scala.collection.TraversableLike
import scala.concurrent.Future

trait AsyncMap[A, +B]
  extends Traversable[(A, B)]
  with TraversableLike[(A, B), AsyncMap[A, B]]
{

  def empty: AsyncMap[A, B]

  // This is the main difference from scala.collection.Map (an AsyncMap doesn't
  // block while it checks if it contains an element for a given key).
  def get(key: A): Future[Option[B]]

  def +[B1 >: B](kv: (A, B1)): AsyncMap[A, B1]

}

编译上面的代码会给我一个错误:

error: overriding method newBuilder in trait TraversableLike of type => scala.collection.mutable.Builder[(A, B),AsyncMap[A,B]];
 method newBuilder in trait GenericTraversableTemplate of type => scala.collection.mutable.Builder[(A, B),Traversable[(A, B)]] has incompatible type
trait AsyncMap[A, +B]
      ^

认为抱怨的是GenericTraversableTemplate 有一个具体的newBuilder实现,其签名与之不兼容 TraversableLike正在寻找的那个。我不明白的是我是怎么做的 可以绕过这个。

实施newBuilder: Builder[(A, B), Traversable[(A, B)]]会产生这一点 错误:

error: overriding method newBuilder in trait TraversableLike of type => scala.collection.mutable.Builder[(A, B),AsyncMap[A,B]];
 method newBuilder has incompatible type
  override def newBuilder: Builder[(A, B), Traversable[(A, B)]] = {
               ^

虽然实施newBuilder: Builder[(A, B), AsyncMap[A, B]]会产生这一点 错误:

error: covariant type B occurs in contravariant position in type => scala.collection.mutable.Builder[(A, B),AsyncMap[A,B]] of method newBuilder
  override def newBuilder: Builder[(A, B), AsyncMap[A, B]] = {
               ^

我认为我采用后一种方法走上正轨,但我不确定如何做到 在这里指定方差。

我也试过让它看起来更像内部收藏品 实施一个 trait AsyncMapLike[A, +B, +This <: AsyncMapLike[A, B, This] with AsyncMap[A, B]], 但这种方法没有结果。

我应该承认我对Scala很新,而且我认为我理解它 类型系统我可能不知道某种类型的操作符或简单的设计模式 解决了这个问题。

非常感谢任何帮助。


可能相关的问题:

1 个答案:

答案 0 :(得分:1)

正如mucaho帮我在上面的评论中发现的那样,事实证明我的问题是由缺少访问修饰符引起的。关于差异的错误消息对我来说仍然没有意义(我已经打开了一个关于它的新问题:Why does scalac only emit variance errors with certain access modifiers?),但当我用具体实现覆盖newBuilder时是protected[this],一切都按预期工作(以前我一直试图公开)。

import scala.collection.mutable.Builder
import scala.collection.TraversableLike
import scala.concurrent.Future

trait AsyncMap[A, +B]
  extends Traversable[(A, B)]
  with TraversableLike[(A, B), AsyncMap[A, B]]
{
  def empty: AsyncMap[A, B]

  def get(key: A): Future[Option[B]]

  def +[B1 >: B](kv: (A, B1)): AsyncMap[A, B1]

  // This works!
  override protected[this] def newBuilder: Builder[(A, B), AsyncMap[A, B]] = ???
}