= Solution
A <covariant type parameter> may be produced but not consumed directly. Widen the output type of `add` through a <lower type bound>. In `compose`, the first parameter of `CoPartialFn` is invariant, so replace its occurrence of `U` by a fresh <type parameter> `W` with <lower type bound> `U`.
``
class CoPartialFn[T,+U](val data: List[(T,U)]) {
require(data.map(_._1).distinct.size == data.size)
def get(x: T): Option[U] =
data.find(p => p._1 == x).map(_._2)
def add[V >: U](x: T, y: V): CoPartialFn[T,V] = {
val old: List[(T,V)] = data.filterNot(p => p._1 == x)
new CoPartialFn[T,V]((x,y) :: old)
}
def remove(x: T): CoPartialFn[T,U] =
new CoPartialFn[T,U](data.filterNot(p => p._1 == x))
def compose[V, W >: U](that: CoPartialFn[W,V]): CoPartialFn[T,V] = {
val pairs: List[(T,V)] = data.flatMap { case (x,y) =>
that.get(y).toList.map(z => (x,z))
}
new CoPartialFn[T,V](pairs)
}
}
``
`List`, `Tuple2`, and `Option` are covariant in their element/output parameters, so `data`, `get`, and `remove` are valid producers. The <variance-position rule> permits `U` in the <lower type bounds>; every value of `U` can be passed to `that.get`, since $U<:W$. No cast or mutation through a widened reference is needed.<Scala 2.11 basic declarations and definitions specification>[Variance Annotations: variance positions and lower-bound widening]{ref}
$$
\boxed{\texttt{add[V >: U]},\qquad\texttt{compose[V, W >: U](that: CoPartialFn[W,V])}.}
$$
The input-key <representation invariant> and the <partial function> semantics are unchanged.
Back to article page