#list #transform #flatten #either #arrow-kt
Вопрос:
Я получаю an Either<E, List<A>>
от вызова функции, и мне нужно преобразовать List<A>
его в a List<B>
. Преобразование каждого A возвращает an Either<E,B>
, так что мой результат равен an Either<E, List<Either<E,B>>>
. Как я могу превратить Either<E, List<Either<E,B>>>
его в Either<E, List<B>>
,
- если все преобразования завершились успешно (должно привести к
Either.Left
, если одно преобразование завершится неудачно) - создайте
Either<E,List<B>>
объект, содержащий все Bs, для которых преобразование прошло успешно, и игнорирование неудачных преобразований AtoB
Небольшой фрагмент кода ниже:
fun getListOfA(): Either<Exception, List<A>> {
TODO()
}
fun A.transformAtoB(): Either<Exception, B> {
TODO()
}
fun getListB(): Either<Exception, List<B>> {
return getListOfA().map {
// this is now Either<Exception, List<Either<Exception, B>>>
listOfA -> listOfA.map { it.transformAtoB() }
// ????? => Either<E, List<B>>
}
}
Ответ №1:
Существует вызываемая функция traverseEither
, которая позволяет вам выполнять такого рода операции.
public inline fun <E, A, B> Iterable<A>.traverseEither(f: (A) -> Either<E, B>): Either<E, List<B>>
Для каждого значения A
в Iterable
нем будет вызываться f
, и если все результаты будут Either.Right
, то это приведет к Either.Right<List<B>>
, а в противном случае это приведет к первому Either.Left<E>
, с которым он столкнется.
Чтобы мы могли переписать ваш фрагмент:
fun getListOfA(): Either<Exception, List<A>> = TODO()
fun A.transformAtoB(): Either<Exception, B> = TODO()
fun getListB(): Either<Exception, List<B>> =
getListOfA().flatMap { listOfA ->
// this is now Either<Exception, List<Either<Exception, B>>>
listOfA.traverseEither { it.transformAtoB() }
}
Он также существует для Validated
и т. Option
Д https://arrow-kt.io/docs/apidocs/arrow-core/arrow.core/kotlin.collections.-итерируемые/индексируемые.html#расширения-для-kotlincollectionsiterable
И вы также можете найти параллельные варианты внутри сопрограмм Arrow Fx
Комментарии:
1. .map{} необходимо изменить на .flatMap {}, чтобы получить либо<E, список<B><B>>. Но в остальном это именно то решение, которое я искал!
2. Если преобразование вернет опцию<B> вместо любого из них, например, в A. transformToB():Опция<B><B>, как это можно обойти?
3. Есть также
traverseOption
, иtraverseValidated
и т. д. Вы можете найти их все по ссылке, опубликованной в этом посте.4. Извините, но я не вижу никакой функции traverseOption(). Я читал связанную страницу раньше, но в описании также нет опции обхода. Существует функция traverse(), функция traverseEither() и функция traverseValidated(). В списке<A> я могу выполнить только обход/проверку пройденного. Мне не хватает, чтобы включить сюда еще одну библиотеку? Я могу применить filterOption() к списку<Опция<B><B>>, который делает то же самое, игнорируя все «Нет». Но если я хочу потерпеть неудачу на Nones, по-видимому, единственный доступный вариант-превратить опцию в Либо, а затем применить traverseEither()
5. Мои извинения,
traverseOption
добавлен только с тех пор0.13.3
, и1.0.0
который должен быть выпущен в сентябре 2021 года (позже на этой неделе написания).