Стрелка-kt: как превратить любой<E, Список<Либо>> в Любой> > <E, Список>

#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 года (позже на этой неделе написания).