Midiparse
Midiparse

Reputation: 4781

Do I have to specify type variance in subtypes?

Let's say I have a trait

sealed trait Expr[-InT, +OutT] {
  def apply(lhs: InT): OutT
}

I want to create a subtype And contravariant in InT. Do I have to implement it as this (1):

type BExpr[-InT] = Expr[InT, Boolean]

final case class And[-InT](exp: BExpr[InT], exps: BExpr[InT]*) extends BExpr[InT] {
  def apply(lhs: InT) = exps.foldLeft(exp.apply(lhs))(_ & _(lhs))
}

Or is the following enough (2)?

type BExpr[InT] = Expr[InT, Boolean]

final case class And[InT](exp: BExpr[InT], exps: BExpr[InT]*) extends BExpr[InT] {
  def apply(lhs: InT) = exps.foldLeft(exp.apply(lhs))(_ & _(lhs))
}

Thanks

Upvotes: 4

Views: 53

Answers (1)

Jasper-M
Jasper-M

Reputation: 15086

Well, let's do a little test.

scala> trait A[+T]
defined trait A

scala> trait B[T] extends A[T]
defined trait B

scala> def b1: B[String] = ???
b1: B[String]

scala> def b2: B[AnyRef] = b1
<console>:13: error: type mismatch;
 found   : B[String]
 required: B[AnyRef]
Note: String <: AnyRef, but trait B is invariant in type T.
You may wish to define T as +T instead. (SLS 4.5)
       def b2: B[AnyRef] = b1
                           ^

scala> def a: A[AnyRef] = b1
a: A[AnyRef]

scala> trait B[+T] extends A[T]
defined trait B

scala> def b3: B[String] = ???
b3: B[String]

scala> def b4: B[AnyRef] = b3
b4: B[AnyRef]

So yes it does make a difference. If you want to expose your subtype to the world, not make it a private internal implementation, it's probably better to add the proper variance annotations.

Upvotes: 3

Related Questions