Reputation: 2294
I'm having a dastard of a time implementing a Map in scala typed to (for our purposes) [String, Set[Foo]] that provides extra operations for the Foos in the values. The actual implementation is more complicated than presented below, but this is the gist. I need a class that implements all the Map-like functions and provides the extra-and-above aggregation on the collections that are the values of the map. The extends Map with MapLike patterns I've seen, particularly this, are not working.
What I've got so far:
import scala.collection.{immutable, Map, MapLike}
class Foo(a:Int)
class CustomMap
(val underlying:Map[String,Set[Foo]] = Map[String,Set[Foo]]())
extends Map[String, Set[Foo]] with MapLike[String, Set[Foo], CustomMap] {
override def empty = new CustomMap(underlying.empty)
def -(key: String) = new CustomMap(underlying - key)
def +[B1 >: Set[Foo]](kv: (String, B1)): scala.collection.Map[String,B1] = new CustomMap(underlying + (kv._1 -> kv._2))
def get(key: String): Option[Set[Foo]] = underlying.get(key)
def iterator: Iterator[(String, Set[Foo])] = underlying.iterator
override def size = underlying.size
def getAllFoos() = underlying.values.flatten.toSet
}
val cm1:CustomMap = new CustomMap(Map("a" -> Set(new Foo(1))))
val cm2:CustomMap = cm1 + ("a" -> Set(new Foo(2)))
println(cm2.getAllFoos)
There are issues both with the + method and accessing the extra aggregate methods.
Any pointers?
file.scala:12: error: type mismatch;
found : B1
required: Set[this.Foo]
def +[B1 >: Set[Foo]](kv: (String, B1)): scala.collection.Map[String,B1] = new CustomMap(underlying + (kv._1 -> kv._2))
^
file.scala:24: error: type mismatch;
found : scala.collection.Map[String,Set[this.Foo]]
required: this.CustomMap
val cm2:CustomMap = cm1 + ("a" -> Set(new Foo(2)))
^
two errors found
Upvotes: 3
Views: 945
Reputation: 13667
+
can't return a CustomMap
because sometimes B1
won't be a Set[Foo]
but some other supertype of Set[Foo]
. This is the source of your errors. Map
and MapLike
are meant for classes that provide a map implementation that could safely have any value added to it and will return a usable Map
. So a Map[String, Set[Foo]]
can always have an ("", 5)
put into it and become a Map[String, Any]
.
You can eliminate the wrapper around your underlying
, as well as avoid these problems, by using the "pimp-my-library" pattern:
implicit class FooSetMap(val map: Map[String,Set[Foo]]) extends AnyVal {
def getAllFoos = map.values.flatten.toSet
}
If you are willing to use a mutable map, take a look at collection.mutable.MultiMap
. It is a mixin trait which adds extra methods to subtypes of mutable.Map[A, mutable.Set[B]]
for working with multimaps - you could do something similar for your needs.
Upvotes: 3