In part 8, we will look at operations on arbitrary arity tuples over a type constructor. These are higher-kinded heterogeneous lists, which I’ll KLists. To motivate why we might want such a structure, we’ll start with Tuple2 for simplicity.
Consider some basic transformations on the elements of a Tuple2. If we want to apply the same function to both elements, we can require that the elements have a common supertype and that the function operates on that supertype:
def map1[A <: C,B <: C,C,D](t: (A,B), f: C => D): (D,D) = (f(t._1), f(t._2)) scala> map1( ("3", true), (_: Any).hashCode ) res1: (Int, Int) = (51,1231)
Or, we might want to supply two separate functions to operate on each component independently:
def map2[A,B,C,D](t: (A,B), f: A => C, g: B => D): (C,D) = (f(t._1), g(t._2)) scala> map2( (1, true), (_: Int) + 1, ! (_: Boolean) ) res3: (Int, Boolean) = (2,false)
Now, consider a Tuple2 where the types of both components are created by the same type constructor, such as (List[Int], List[Boolean])
or (Option[String], Option[Double])
.
One useful operation on such a structure looks like:
def transform[F[_], A, B, G[_]] (k: (F[A], F[B]), g: F ~> G): (G[A], G[B]) = ( g(k._1), g(k._2) )
This applies the provided natural transformation to each element, preserving the underlying type parameter, but with a new type constructor. As an example, we can apply the toList transformation we defined in part 7:
val toList = new (Option ~> List) { def apply[T](opt: Option[T]): List[T] = opt.toList } // these are so that we get Option[T] as the type // and not Some[T] or None def some[T](t: T): Option[T] = Some(t) def none[T]: Option[T] = None scala> transform((some(3), some("str")), toList) res5: (List[Int], List[java.lang.String]) = (List(3),List(str)) scala> transform((some(true), none[Double]), toList) res6: (List[Boolean], List[Double]) = (List(true),List())
In part 6, we were concerned with the generalization of TupleN to arbitrary arity. In part 8, we will generalize ‘transform’ and related operations to heterogeneous lists over a type constructor.