Reputation: 4781
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
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