Why is flatMap on a Vector[Option[Int]] whose mapper function result is not a Vector[Option[Int]] valid?
Asked Answered
T

2

3

For example,

Vector(Some(1), Some(2), Some(3), None).flatMap{
  n => n
}

produces a Vector(1, 2, 3) instead of giving an error. As I have seen in other languages, flatMap is used when you have a mapper function that produces nesting so I would expect this to be a valid flatMap:

Vector(1, 2, 3).flatMap{
  eachNum => Vector(eachNum)
}

My mapper function produces a Vector which would cause nesting (i.e. Vector(Vector(1), Vector(2), Vector(3), Vector(4))) if I used a map due to the container wrapping. However, flatMap will remove this nesting and flatten it. This makes sense when there is nesting of two identical monads.

However, I do not understand how using a flatMap with a mapper function that returns an Option makes a Vector[Option[Int]] become a Vector[Int]. Is there some sort of transformation going on (I have never seen this before), could someone explain and perhaps point me to some resources?

Thank you very much

Themselves answered 12/1, 2016 at 4:2 Comment(0)
M
8

we can use reify to see what is going on:

scala> import reflect.runtime.universe._
import reflect.runtime.universe._

scala> val v = Vector(Some(1), Some(2), Some(3), None)
v: scala.collection.immutable.Vector[Option[Int]] = Vector(Some(1), Some(2), Some(3), None)

scala> reify { v.flatMap(x => x) }
res0: reflect.runtime.universe.Expr[scala.collection.immutable.Vector[Int]] =
    Expr[scala.collection.immutable.Vector[Int]]($read.v.flatMap(((x) =>
     Option.option2Iterable(x)))(Vector.canBuildFrom))

This is showing us that it is using the option2Iterable conversion to convert the Option to Iterable, and Iterable is a subtype of the GenTraversableOnce type that flatMap is expecting.

Malone answered 12/1, 2016 at 5:8 Comment(2)
I commented on the other answer, just a plug for :replay -Xprint:typer.Alysa
This is a great explanation!Themselves
C
3

The function f passed within the flatMap here:

Vector(Some(1), Some(2), Some(3), None).flatMap{
    n => n
}

Is a function A => GenTraversableOnce[B] as described in the flatMap implementation:

def flatMap[B, That](f : scala.Function1[A, GenTraversableOnce[B]])
                    (implicit bf : CanBuildFrom[Repr, B, That])
                    : That = ???

The function implemented in your example n => n is:

(n: Option[Int]) => n

Where A is Option[Int] and B is Int.

Because CanBuildFrom is defined as trait CanBuildFrom[-From, -Elem, +To]:

  • From is Repr, and in this case Vector
  • Elem is B so Int

The result of flatMap is therefore Vector[Int]

Cotopaxi answered 12/1, 2016 at 4:12 Comment(1)
Compile with -Xprint:typer to see Option.option2Iterable at work.Alysa

© 2022 - 2024 — McMap. All rights reserved.