В чем магия этого кода flatMap?

Я изучаю Scala и в приведенном ниже коде использую flatMap (взято из фильтровать с плоской картой или собирать)

У меня есть

list flatMap {
    case st: String => Some(st)
    case _ => None
}

Он работает на List[Any] и дает List[String]

scala> List(1, "A") flatMap {
     | case st: String => Some(st)
     | case _  => None
     | }
res21: List[String] = List(A)

Теперь я смущен types здесь. Как я и думал, flatMap работает с некоторыми видами monad, которые работают как преобразование из M[M[A]] -> M[A].

Код ниже легко понять,

def flatten(ls: List[Any]): List[Any] = ls flatMap {
    case ms: List[_] => flatten(ms)
    case e => List(e)
}

так как оба случая возвращают List[Any], который по-прежнему имеет тот же тип, что и ls: List[Any].

Но почему Some[String] и None допустимы в flatMap List[Any]?

Кроме того, кажется, что None полностью игнорируется, а не рассматривается как серьезное значение? Я думал, что, возможно, есть какие-то шаги по сжатию, чтобы избавиться от таких значений, например:

[1,2,3].concat([,,2,2])
// => (6) [1, 2, 3, empty × 2, 2]
[1,2,3].concat([,,4]).filter(Boolean)
// => (4) [1,2,3,4]

Может ли кто-нибудь объяснить это? Спасибо!!!


person Wan ChengCheng    schedule 06.07.2018    source источник


Ответы (2)


Как я и думал, flatMap работает с некоторыми видами монад, которые работают как преобразование M[M[A]] -> M[A].

Scala flatMap более универсален (что некоторым не нравится).

Если вы посмотрите на documentation, достаточно, чтобы функция, переданная List#flatMap, возвращала GenTraversableOnce[SomeType], а не List[SomeType]. И хотя Option не расширяет GenTraversableOnce, существует неявное преобразование между ними, которое применяется здесь.

Кроме того, кажется, что None полностью игнорируется, а не рассматривается как серьезное значение?

None соответствует пустой коллекции, Some(x) — одноэлементной коллекции. Итак, у вас есть, например.

Some(1) ++ Some(2) ++ None ++ Some(3) ==
List(1) ++ List(2) ++ List() ++ List(3) ==
List(1,2,3)

Или, говоря вашим языком, у вас не [1,2,3,,,4] (что не имеет смысла), а [[1],[2],[3],[],[],[4]].

person Alexey Romanov    schedule 06.07.2018

flatMap не ограничивается вложенными коллекциями одного типа. Значение, возвращаемое функцией, переданной в flatMap, может быть любым типом коллекции. flatMap возьмет каждый элемент из этой коллекции и добавит его в результирующую коллекцию. Option[T] работает как коллекция из 0 или 1 элементов, поэтому flatMap работает так же, как и с List, Vector, Array или другими коллекциями.

Однако в этом конкретном случае вы бы использовали collect, а не flatMap:

list.collect{ case s: String => s }
person Tim    schedule 06.07.2018