Solution (source code)

= 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.