user3663882
user3663882

Reputation: 7357

Understanding flatMap declaration in List

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).

Upvotes: 5

Views: 146

Answers (2)

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149518

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)

Upvotes: 5

sarveshseri
sarveshseri

Reputation: 13985

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)

Upvotes: 2

Related Questions