5

I just looked at the List.flatMap declaration and was kind of surprised by this.

final override def flatMap[B, That](f: A => GenTraversableOnce[B])
                 (implicit bf: CanBuildFrom[List[A], B, That]): That

Where object List defines:

implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, List[A]] =
    ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]]

So, if we invoke flatMap on a List we will get the List and I don't see any point in That type if it will always be deduced to List[B] (because of the implicit).

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
user3663882
  • 6,957
  • 10
  • 51
  • 92
  • that `implicit` can be overridden with a more local one, that builds to, let's say, a `Vector`, or explicitly passed as a parameter – Chirlo Aug 29 '16 at 10:52
  • See http://stackoverflow.com/a/1728140/5344058 for an example of when it's useful to have `That` being a different type compared to the original collection – Tzach Zohar Aug 29 '16 at 10:56
  • Have you ever seen a `collection.breakout` with flatMap or Map... that is one example of overriding this implicit. – sarveshseri Aug 29 '16 at 11:11

2 Answers2

5

So, if we invoke flatMap on a List[A] we will get the List[A] and I don't see any point in That type if it will always be deduced to List[B]

One thing you're missing is that flatMap isn't actually defined on List[+A]. It is inherited from TraversableLike, which is a trait used by most of Scalas collections. Each of them can supply the implicit CanBuildFrom which may be overridden to supply a different resulting collection.

If you want a little taste of what you can do with a custom CanBuildFrom:

scala> :pa
// Entering paste mode (ctrl-D to finish)

import scala.collection.generic.CanBuildFrom
import scala.collection.immutable._
import scala.collection.mutable
import scala.{List, Vector}

implicit val listToVectorCBF = new CanBuildFrom[List[Int], Int, Vector[Int]] {
  override def apply(from: List[Int]): mutable.Builder[Int, Vector[Int]] = this.apply()
  override def apply(): mutable.Builder[Int, Vector[Int]] = Vector.newBuilder
}

// Exiting paste mode, now interpreting.

scala> List(1,2,3).flatMap(List(_))
res6: Vector[Int] = Vector(1, 2, 3)
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
2

Well... That implicit CanBuildFrom can be used to directly build different type of structure instead of List and thus saving one extra step. lets look at following example,

val list = List(List(1, 2, 3), List(4, 5, 6))

// now if we do a flatmap withtout overriding the implicit CanBuildFrom
val newList = list.flatMap(l => l.map(i => (i,i)))

// new list will be a List[(Int, Int)]
// but what if you wanted a map

val newMap = newList.toMap

// But you actually needed to traverse the list twice in this case

// But we can avoid the second traversal if we chose to override the implicit
val newMap2 = list.flatMap(l => l.map(i => (i,i)))(collection.breakout)
sarveshseri
  • 13,738
  • 28
  • 47