I'm attempting to implement a new collection type which follows the same idioms
as the standard library, but am having trouble figuring out how to handle the
Builder
mechanics. I've read through the excellent "Architecture of Scala
Collections" doc
page,
but it doesn't cover my situation.
Here's a simplified version of what I'm trying to do:
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]
}
Compiling the above code gives me an error:
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] ^
I think what this is complaining about is that GenericTraversableTemplate
has a concrete newBuilder
implementation whose signature is incompatible with
the one that TraversableLike
is looking for. What I don't understand is how I
can get around this.
Implementing newBuilder: Builder[(A, B), Traversable[(A, B)]]
produces this
error:
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)]] = { ^
While implementing newBuilder: Builder[(A, B), AsyncMap[A, B]]
produces this
error:
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]] = { ^
I think I'm on the right track with the latter approach, but am not sure how to specify the variance here.
I've also tried making this look more like the internal collections by
implementing a
trait AsyncMapLike[A, +B, +This <: AsyncMapLike[A, B, This] with AsyncMap[A, B]]
,
but that approach hasn't borne any fruit.
I should admit that I'm pretty new to Scala and while I think I understand its type system I could be unaware of some type operator or simple design pattern that solves this.
Any help would be greatly appreciated.
Possibly-related questions:
As mucaho helped me discover in the comments above, it turns out that my problem was caused by a missing access modifier. The error message about variance still doesn't make sense to me (I've opened a new question about it: Why does scalac only emit variance errors with certain access modifiers?), but when I override
newBuilder
with a concrete implementation whose access isprotected[this]
, everything works as expected (previously I had been trying to make it public).