Scala filter by type parameter and return construct return type with type constructor

Viewed 88

I have the following working code

case class FilterType[A](t: GenTraversable[A]) {
  def filterType[B](implicit tag: ClassTag[B]): GenTraversable[B] = t.flatMap {
    case element: B => Some(element)
    case _          => None
  }
}

With this definition I am able to filter the following as expected

trait Demo
case class Demo1(a: String) extends Demo
case class Demo2(a: Int)    extends Demo

val input: Seq[Demo]              = Seq(Demo1("hello"), Demo2(2))
val output: GenTraversable[Demo1] = FilterType(input).filterType[Demo1]

I would like to improve on that and take a type constructor as a parameter as well so that instead of returning a GenTraversable I can return the same type as input (in this case a Seq). My attempt was the following

case class FilterType2[T[_]: GenTraversable, A](t: T[A]) {
  def filterType[B](implicit tag: ClassTag[B]): T[B] = t.flatMap {
    case element: B => Some(element)
    case _          => None
  }
}

However I'm getting the following compilation errors:

Error 1: type T takes type parameters
Error 2: value flatMap is not a member of type parameter T[A]
1 Answers

For your first error:

GenTrasversable is not a context/typeclass. You are expecting a subclass of GenTrasversable, not a context of that, so you should use <: instead of :.

For your second error:

flatMap does not belong to GenTrasversable. It belongs to GenTraversableLike: doc

Working code:

You also need CanBuildFrom in Scala 2.12 for constructing a generic collection.

case class FilterType[T[A] <: GenTraversableLike[A, T[A]], A](t: T[A]) {
  def filterType[B](implicit tag: ClassTag[B], bf: CanBuildFrom[T[A], B, T[B]]): T[B] = t.flatMap {
    case element: B => Some(element)
    case _          => None
  }
}

Full code: code

Related