I would like to derive from Scala's immutable Map. It is defined as such:
trait Map[A, +B]
Unfortunately, my implementation needs to be invariant in B. I tried the following, but without success:
def +(kv : (A, B)) : MyMap[A, B] = { ... }
override def +[B1 >: B](kv : (A, B1)) : MyMap[A, B1] =
throw new IllegalArgumentException()
Maybe there is a trick with @uncheckedVariance
?
The trouble is that if you derive an invariant version from an immutable map, you'll break type safety. For example:
val dm = DiotMap(1 -> "abc")
val m: Map[Int, Any] = dm
This declaration is valid, because Map
is covariant. If your collection cannot handle covariance, what will happen when I use m
?
Getting rid of covariance altogether would of course be unsound, and is not allowed.
Given m: Map[A, String]
, and v : Any
, you can do val mm : Map[A, Any] = m + v
. This is what Map
definition says, and all implementors must follow. Your class may be invariant, but it must implement the full covariant interface of Map.
Now redefining +
to throw an error is a different story (not very sound yet). The problem with your new +
method is that after generics erasure, it has the same signature than the other +
method. There is a trick: add in implicit parameter, so that you have two parameters in the signature, which makes it different from the first one.
def +(kv : (A,B))(implicit useless: A <:< A) : MyMap[A,B]
(it doesn't really matter what implicit parameter you're looking for, as long as one is found. implicit useless: Ordering[String]
works just as well)
Doing that, you have the usual problem with overloading. If you add a B without the compiler knowing it to be so, the failing method will be called. It might be better to perform a type check there so that B instances are accepted whatever. That would require getting a Manifest[B] in your map.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With