Understanding flatMap declaration in List
Asked Answered
C

2

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

Consultant answered 29/8, 2016 at 10:46 Comment(3)
that implicit can be overridden with a more local one, that builds to, let's say, a Vector, or explicitly passed as a parameterLopeared
See https://mcmap.net/q/53551/-is-the-scala-2-8-collections-library-a-case-of-quot-the-longest-suicide-note-in-history-quot-closed for an example of when it's useful to have That being a different type compared to the original collectionByer
Have you ever seen a collection.breakout with flatMap or Map... that is one example of overriding this implicit.Dulciana
G
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)
Goble answered 29/8, 2016 at 10:51 Comment(3)
But can I supply my own CanBuildFrom in order to get something that is not even a Seq?Consultant
@Consultant Nothing is stopping you from supplying your own. It makes it easier for derived classes to simply provide their own CBF implicit instead of re-implementing the method.Goble
@Consultant I've added a little example to demonstrate.Goble
D
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)
Dulciana answered 29/8, 2016 at 11:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.