Paul
Paul

Reputation: 77

Covariance and Scala Collections

I'm trying to get my head around the covariance of Scala's collections. I have the following:

abstract class MediaFormat{
    def name:String
    def status:String
}

case class H264_high(status:String="on") extends MediaFormat {
    def name = "h264_high"
}
case class H264_med(status:String="on") extends MediaFormat {
    def name = "h264_med"
}
case class H264_low(status:String="on") extends MediaFormat {
    def name = "h264_low"
}
case class H264_syndication(status:String="off") extends MediaFormat {
    def name = "h264_syndication"
}

What I wanted to do was have a set of all of these formats because I need a collection where each format only occurs once, so I tried:

object MediaFormat {
    val allFormats:Set[MediaFormat] = Set(H264_high,H264_low)
}

This gave me a compile time exception because, as I now understand, Set is invariant.

So I think, well I guess I'll just have to use a List and manage the repeated values myself

but then I try this:

object MediaFormat {
    val allFormats:List[MediaFormat] = List(H264_high,H264_low)
}

because as I understand it, List is covariant, but that still doesn't compile.

Can someone help me understand what I should do to get a collection of my formats?

Upvotes: 1

Views: 278

Answers (1)

oxbow_lakes
oxbow_lakes

Reputation: 134270

It doesn't compile because you are referencing the companion object (module), not the case classes! The compile error (which you should have posted) is nothing to do with variance. It will work, with Set if you do this:

val allFormats: Set[MediaFormat] = Set(H264_high(), H264_low())
                                                ^^          ^^

Or alternatively;

val allFormats = Set[MediaFormat](H264_high(), H264_low())

However, it makes no sense for these to be case classes given your description of the problem; I would just make them modules, i.e.

case object H264_syndication extends MediaFormat {
  def status = "off"
  def name = "h264_syndication"
}

Then your original code will work just fine. Or I would make them vals as follows:

case class MediaFormat(status: String, name: String)
val H264_syndication = MediaFormat(status ="off", name = "h264_syndication")

I think this would be my preference; I rarely use abstract classes any more to be honest (normally, I am dishonest).


Explanation: Covariance means the following:

G[S] <: G[T] iff S <: T

The fact that Set is invariant, means that a Set[S] is not a subtype of Set[T] (for S <: T), but it does not mean that such a Set[T] may not contain elements of type S.

Upvotes: 7

Related Questions