= Solution
Use the <representation invariant> that each input occurs at most once in `data`. It is an immutable finite <partial function>, so extending or removing an input returns a new object. The source comment `get(x) returns Some(x)` contradicts both the <type signature> `Option[U]` and the input-output semantics: it must return `Some(y)` when $f(x)=y$.
``
class PartialFn[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(x: T, y: U): PartialFn[T,U] =
new PartialFn[T,U]((x,y) :: data.filterNot(p => p._1 == x))
def remove(x: T): PartialFn[T,U] =
new PartialFn[T,U](data.filterNot(p => p._1 == x))
def compose[V](that: PartialFn[U,V]): PartialFn[T,V] = {
val pairs: List[(T,V)] = data.flatMap { case (x,y) =>
that.get(y).toList.map(z => (x,z))
}
new PartialFn[T,V](pairs)
}
}
``
`get` returns an <optional value> rather than inventing a result outside the <domain of a function>. `add` removes any old pair for its input before inserting the replacement; `remove` retains exactly the other pairs. In `compose`, undefined intermediate outputs contribute an empty <list>; defined outputs contribute one pair. Hence uniqueness of input keys is preserved.
The printed composition order is deliberately followed:
$$
\boxed{\mathrm{this.compose(that)}=\mathrm{that}\circ\mathrm{this},\qquad x\mapsto\mathrm{that}(\mathrm{this}(x)).}
$$
This is the stated <function composition>, even though some libraries use the name `compose` for the opposite order.
Back to article page