Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 748
user3663882 Avatar asked Aug 29 '16 10:08

user3663882


2 Answers

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)
like image 136
Yuval Itzchakov Avatar answered Nov 08 '22 03:11

Yuval Itzchakov


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)
like image 22
sarveshseri Avatar answered Nov 08 '22 05:11

sarveshseri