I am trying to define a map from instances of a sealed trait. In the following code, Scala seems to infer the key type as Product with Serializable with Day
:
object Test extends App {
sealed trait Day
case object Sunday extends Day
case object Monday extends Day
case object Tuesday extends Day
val m: Map[Day, Int] = Map(Sunday -> 17, Monday -> 4).withDefaultValue(0)
}
This does not compile:
Test.scala:7: error: type mismatch;
found : scala.collection.immutable.Map[Product with Serializable with Test.Day,Int]
required: Map[Test.Day,Int]
Note: Product with Serializable with Test.Day <: Test.Day, but trait Map is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: Test.Day`. (SLS 3.2.10)
val m: Map[Day, Int] = Map(Sunday -> 17, Monday -> 4).withDefaultValue(0)
I can change the key type in the definition of m
, but that would mean repeating Product with Serializable with Day
in many places. Another option I found was to change the definition of the trait to:
sealed trait Day extends Product with Serializable
As there are many advantages for using sealed traits and case objects instead of enums, I am wondering what would be a good approach to put them as keys in a map.
Because Map
needs the keys to have properties which are defined in Product
and Serializable
, so Scala implicitly creates anonymous class
which extends your class with Product
and Serializable
which provides default implementations of equals
and hash
.
object Test extends App {
trait PS extends Product with Serializable
sealed trait Day extends PS
case object Sunday extends Day
case object Monday extends Day
case object Tuesday extends Day
val m: Map[Day, Int] = Map(Sunday -> 17, Monday -> 4).withDefaultValue(0)
}
The fundamental reason you are seeing this is that
the compiler infers by default the most specific type, which is in this case Product with Serializable with Test.Day
. The Product
and Serializable
classes are implicitly implemented by all case classes and case objects in Scala, so all case objects have them in common. Hence, this is truly the most specific common type of Sunday
and Monday
,
traits on the other hand, do not implement Product
nor Serializable
.
Thus, when you require somewhere Day
, the more specific, inferred type will not do (also because, as the error message indicates, the K
type parameter of Map
is invariant.)
One way, like indicated by the answer by Sarvesh Kumar Singh, is to have your trait extend Product
and Serializable
, but in my view the better one (pointed out by Rüdiger Klaehn) is to tell the compiler that you are actually fine with the more general type Map[Day, Int]
by making the type explicit:
val m = Map[Day, Int](Sunday -> 17, Monday -> 4).withDefaultValue(0)
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