flatten
не включает в себя часть "карты" flatMap
. Думайте об этом как о просто сглаживании вложенных структур. Option[Option[Int]]
становится просто Option[Int]
или List[List[Int]]
становится List[Int]
(путем объединения элементов отдельных списков).
Напротив, отображение изменяет элементы, содержащиеся в структурах. Итак
Some(4).map(_ + 1) // evaluates to Some(5)
Иногда функция, переданная в map
, возвращает экземпляр самой базовой структуры. Допустим, у вас есть необязательный идентификатор, и если он установлен, вы хотите просмотреть его в базе данных, не зная, присутствует ли запись, поэтому ваша функция также возвращает Option
:
val optionalId: Option[String] = ???
def getRecord(id: String): Option[Record] = ???
val naiveResult: Option[Option[Record]] = optionalId.map(getRecord)
Обычно это не то, что вы хотите. По сути, у вас есть Option[Record]
, и дополнительное вложение не требуется. Так что вы бы позвонили flatten
сразу после map
:
optionalId.map(getRecord).flatten // evaluates to an Option[Record]
Теперь flatMap
по сути является комбинацией обоих в одном методе:
optionalId.flatMap(getRecord) // also yields an Option[Record]
Применение flatMap
не ограничивается коллекциями, но гораздо более общим. Другой пример, где это пригодится, это фьючерсы. Допустим, у нас нет необязательного идентификатора, но есть Future [String], который представляет вычисление, которое в конечном итоге даст идентификатор. У нас также есть метод, который дает нам Future[Record]
для идентификатора. Теперь мы можем получить Future[Record]
из Future[String]
следующим образом:
val idFuture: Future[String] = ???
def getRecord(id: String): Future[Record] = ???
val recordFuture: Future[Record] = idFuture.flatMap(getRecord)