TL; DR Всякий раз, когда ваш груз находится внутри корабля, перевозимого другим кораблем - нанесите на карту этот корабль!
Программирование часто связано с преобразованием данных из одного типа в другой. Vavr предоставляет метод map
, который вызывается во время так называемого счастливого пути: вы сопоставляете успех Try
, значение Option
, право Either
, успешный результат Future
и, наконец, элемент List
.
Каждый раз, когда вы вводите map
, ваша среда IDE предоставляет вам список предложений, и обычно среди них присутствует метод flatMap
. Если вам интересно, когда и как использовать эту загадочную вещь, вот несколько примеров.
1) flatMap тот список списков
В этом примере flatMap
используется для первого map
каждого элемента listOfLists
List
, называемого innerList
, другому List
. Каждый innerList
элемент, List
из String
s остается нетронутым - это то, что означает x -> x
. Другой способ вернуть ввод функции в map
операции - использовать java.util.function.Function.identity()
.
Вторая задача flatMap
- сгладить результат. В случае List
из List
это означает возврат одного List
с элементами из всех внутренних List
: [[a, b], [c, d], [e, f, g]]
становится [a, b, c, d, e, f, g]
.
Немного более сложная версия этого примера - применить функцию карты к каждому внутреннему элементу List
, например toLowerCase()
.
Подводя итог: flatMap
на List
из List
s применяет map
функцию к каждому элементу внешнего List
и возвращает один List
.
2) flatMap, список опций
Точно так же flatMap
может сгладить List
из Option
из String
, чтобы вернуть List
из String
. Здесь хорошо то, что обрабатывается (отображается) только успех - в случае Option
успех - это когда он определен.
Очевидно, мы также можем применить функцию map
к каждому элементу, заключенному в Option. Вот метод toLowerCase()
:
Подводя итог: flatMap
на List
из Option
s применяет map
функцию к каждому определенному Option
и возвращает по одному List
из каждого обернутого элемента.
3) flatMap, список попыток
Последний базовый пример, и мы готовы перейти к более интересным вещам. Сначала отображается List
из Try
объектов, каждый из которых завершает неудачу или успех (значение String
). Короче говоря, Function.identity()
оставляет успехи и отфильтровывает любые неудачи, оставляя нам List
успешных Try
объектов. Наконец, этот список сглаживается, что означает извлечь успешные значения из объектов Try
и вернуть их как List
.
Подводя итог: flatMap
, примененный к List
из Tries
, запускает map
функцию для каждого Try.Success
и возвращает List
каждого обернутого элемента.
Это очень простые примеры выполнения тривиальных операций с картой. Они просто сглаживают коллекцию коллекций, возвращая одну единственную коллекцию. Это может быть применено к другим функциональным структурам данных, таким как Array
, Vector
, Queue
, Set
и Map
.
4) flatMap, которая пытается из попыток
Как только вы привыкнете к функциональному программированию с помощью Vavr и начнете использовать Try
в общедоступных методах, вскоре вы можете столкнуться с гораздо более распространенным сценарием, когда из контекста Try
вы вызываете другой метод, возвращающий Try
:
Метод wrapThrowOnXYZ()
возвращает Try
. В случае успеха мы сопоставляем обернутое значение другому Try
, вызывая anotherTryWrapper()
. И это оставляет нам Try
из Try
. Возможны три результата:
- внешний Try
- сбой,
- внешний Try
- успех, но внутренний Try
- сбой,
- внешний Try
- успех и внутренний Try
- это успех.
Чтобы извлечь результат (Try
из Try
), мы не можем просто вызвать get()
на внешнем Try
. Сначала нам нужно проверить, успешно ли работает isSuccess()
. В противном случае мы могли бы закончить с выдачей исключения, если Try
завершит ошибку, и будет следовать наш профессиональный стиль программирования «не бросать, вернуть попробовать».
Это можно исправить с помощью flatMap
:
flatMap
либо вернет успех, заключив String
, либо один из двух возможных сбоев: исключение из wrapThrowOnXYZ()
или исключение из anotherTryWrapper()
.
Подводя итог: flatMap
на Try
из Tries
возвращает Try.Success
, если все Tries
успешны или первые Try.Failure
, встреченные в цепочке.
5) flatMap тот вариант варианта
В следующем примере показан очень похожий подход, только с Option
s.
Как только методы возвращают Option
s, обертывая значение или Option.None
, вы столкнетесь с такой ситуацией: из контекста Option
вы предоставляете средство сопоставления, возвращающее также Option
:
И снова у нас остается 3 возможных результата:
- внешний Option
- это None
,
- внешний Option
- это Some
, а внутренний Option
- это None
,
- внешний Option
это Some
, а внутренний Option
- это Some
.
Мы не можем вызвать get()
на внешнем Option
, чтобы распаковать внутреннее Option
, не вызвав сначала isDefined()
. В противном случае возникает исключение, если Option
является None
. Мы хотим, чтобы только один Option
был либо None
, либо Some
. И вот flatMap
снова светится.
Подводя итог: flatMap
, примененный к Option
другого Option
, приводит к Option.Some
, если все Option
равны Some
, или Option.None
в противном случае.
6) FlatMap that Either of Either (Либо из Либо), либо
Как указывалось ранее, в случае успеха map применяет функцию сопоставления. В случае Either
это значение Right
. Сказав это, давайте посмотрим, как применить flatMap
на Either
.
outerEither
Either
- это
- Either.Right
обертывание age
String
, если age
не равно нулю,
- Either.Left
обертывание «пусто», если age
равно нулю.
Применение flatMap
возвращает
- Either.Left
упаковка «пуста», если outerEither
Either.Left
,
- Either.Left
упаковка String
, которая не может быть проанализирована в Integer
,
- Either.Right
упаковка Integer
, если age
может быть успешно проанализирован.
Подводя итог: flatMap
на Either
другого Either
приводит к Either.Right
, если все Either
равны Right
, или Either.Left
, завершающий первый встреченный Left
.
Все примеры доступны на GitHub, а также имеется набор тестов, позволяющий поэкспериментировать с различными входными данными.
Подведем итоги: есть много способов flatMap
можно использовать для обработки данных. Однако теперь должно быть ясно, что значит сопоставить элемент List
, Try.Success
, Option.Some
, завершенный Future
или Either.Right
, а затем сгладить результат.
Ищете экспертов по Scala и Java?
Мы заставим технологии работать на ваш бизнес. Посмотреть проекты, которые мы успешно реализовали.