Search code examples
scalagenericshigher-kinded-types

Mapping over collections and returning the same type of container as the input


For learning purposes, I am trying to define a wrapper class called DoubleMap which provides the method mapBoth. It essentially takes two functions f and g, where the domain of g is the co-domain of f. The composition of this function (g o f) should then be mapped of the container wrapped by DoubleMap

This is my current code:

implicit class DoubleMap
[A, B, C, F[X] <: List[X]] // just List for now
(xs: F[A])(implicit cbf: CanBuildFrom[F[A], C, F[C]]) {
  def mapBoth(f: A => B)(g: B => C): F[C] =
    xs.map(f andThen g).to[F]
}

However, when I want to use the method like this:

List(true, false, false).mapBoth(!_)(!_)

I get a cryptical error message about the type mismatch between the (found) type CanBuildFrom[List[_], Nothing, List[Nothing]] and the (required) type CanBuildFrom[List[Boolean], C, List[C]]

Why does the compiler infer the first type?


Solution

  • Your call expands to

    DoubleMap(xs).mapBoth(!_)(!_)
    

    so all of DoubleMap's type parameters and cbf need to be inferred before the mapBoth call is handled. To fix it, just move these parameters to mapBoth:

    implicit class DoubleMap
    [A, F[X] <: List[X]] // just List for now
    (xs: F[A]) {
      def mapBoth[B, C](f: A => B)(g: B => C)(implicit cbf: CanBuildFrom[F[A], C, F[C]]): F[C] =
        xs.map(f andThen g).to[F]
    }
    

    Then B and C are determined from f and g, and cbf from F, A and C.