Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does "flatMap" work with sequences of Option type in Scala?

I can't figure out how the Scala compiler figures out how to use flatMap with a sequence of Options.

If I use a flatMap on a sequence of sequences:

println(Seq(Seq(1), Seq()).flatMap(a => a)) // List(1)

it will concatenate all nested sequences

The same happens if I use it with a sequence of Options:

println(Seq(Some(1), None).flatMap(a => a)) // List(1)

So the flatMap treats Option as a collection in this case. The question is why does this work? The flatMap has the following definition:

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

Meaning that it expects a function that returns an instance of GenTraversableOnce, but Option does not inherit GenTraversableOnce. It only inherits Product and Serializable, and Product inherits Equals.

How does Scala compiler can use the flatMap on the sequence of Options in this case?

like image 547
Ivan Mushketyk Avatar asked Oct 08 '17 10:10

Ivan Mushketyk


People also ask

How does flatMap work in Scala?

In Scala, flatMap() method is identical to the map() method, but the only difference is that in flatMap the inner grouping of an item is removed and a sequence is generated. It can be defined as a blend of map method and flatten method.

What is the difference between MAP and flatMap in Scala?

The flatMap() method is similar to the map() method, but the only difference is that in flatMap, the inner grouping of an item is removed and a sequence is generated. The flatMap method acts as a shorthand to map a collection and then immediately flatten it.

Why is flatMap useful?

Using flatMap() is useful when you want to add and remove items during a map() , as it can map many to many items by handling each input item separately, versus map() itself that is always one-to-one. This means the resulting array can grow during the mapping, and it will be flattened afterward.

What is a SEQ in Scala?

Scala Seq is a trait to represent immutable sequences. This structure provides index based access and various utility methods to find elements, their occurences and subsequences. A Seq maintains the insertion order.


1 Answers

Your observation is right. In this case, if the compiler can't match the type, it looks for an implicit conversion and finds one in Option's companion object:

import scala.language.implicitConversions

/** 
    An implicit conversion that converts an option to an iterable value
*/

implicit def option2Iterable[A](xo: Option[A]): Iterable[A] = xo.toList

This makes it possible to treat Options as Iterables.


Also, your code can be simplified using flatten:

Seq(Some(1), None).flatten
like image 167
Grzegorz Piwowarek Avatar answered Sep 24 '22 22:09

Grzegorz Piwowarek