Take a look at the following code:
case class MyTypeConstructor[T[_]: Seq, A](mySeq: T[A]) {
def map[B](f: A => B): T[B] = mySeq.map(f) // value map is not a member of type parameter T[A]
}
case class MyTypeBounds[T[A] <: Seq[A], A](mySeq: T[A]) {
def map[B](f: A => B): T[B] = mySeq.map(f)
}
Ideally both would do the same thing, just define a dummy map
that calls the map
method from Seq
. However, the first one does not event compile while the second one works (actually the second one doesen't work either but I am omitting things for simplicity).
The compilation error I get is that T[A]
does not have a member map
, but I am stranged because the type constructor T
should return a Seq
(which does have map
).
Can anyone explain me what is conceptually different between these two implementations?
what is conceptually different between these two implementations?
We can constrain polymorphic type parameters either using subtyping or type class approach
scala> case class Subtyping[T[A] <: Seq[A], A](xs: T[A]) {
| def map[B](f: A => B) = xs.map(f)
| }
|
| import scala.collection.BuildFrom
|
| case class TypeClassVanilla[T[x] <: IterableOnce[x], A](xs: T[A]) {
| def map[B](f: A => B)(implicit bf: BuildFrom[T[A], B, T[B]]): T[B] =
| bf.fromSpecific(xs)(xs.iterator.map(f))
| }
|
| import cats.Functor
| import cats.syntax.all._
|
| case class TypeClassCats[T[_]: Functor, A](xs: T[A]) {
| def map[B](f: A => B): T[B] =
| xs.map(f)
| }
class Subtyping
import scala.collection.BuildFrom
class TypeClassVanilla
import cats.Functor
import cats.syntax.all._
class TypeClassCats
scala> val xs = List(1, 2, 3)
val xs: List[Int] = List(1, 2, 3)
scala> Subtyping(xs).map(_ + 1)
val res0: Seq[Int] = List(2, 3, 4)
scala> TypeClassCats(xs).map(_ + 1)
val res1: List[Int] = List(2, 3, 4)
scala> TypeClassVanilla(xs).map(_ + 1)
val res2: List[Int] = List(2, 3, 4)
They are different approaches to achieving the same thing. With type class approach perhaps we do not have to worry as much about organising inheritance hierarchies, which as system grows in complexity, might lead us to start artificially forcing things into hierarchy.