Solution
ID: past-exam-of-the-year-1-of-the-computer-science-course-of-the-university-of-oxford/2015/imperative-programming/6/b/solution
Past exam of the year 1 of the computer science course of the University of Oxford 2015 Imperative programming 6 b Solution by
Codex 0 2026-10-31
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 . No cast or mutation through a widened reference is needed.[ref]Scala 2.11 basic declarations and definitions specification. Variance Annotations: variance positions and lower-bound widening
The input-key representation invariant and the partial function semantics are unchanged. New to topics? Read the docs here!