Kotlin arrow-kt,将任一集合映射到任一集合的功能方法
Kotlin arrow-kt, functional way to map a collection of either to an either of a collection
我最近一直在使用 kotlin arrow,我 运行 遇到了一个让我卡住的特定用例。
假设我有一些对象的集合,我想使用转换函数将其转换为另一种数据类型。我们还假设这个转换函数有失败的能力——但它不会抛出异常,它只会 return 一个 Either,其中 Either.Left()
是失败而 Either.Right()
是映射目的。处理此用例的最佳方法是什么?下面是一些示例代码:
val list: Collection<Object> // some collection
val eithers: List<Either<ConvertError, NewObject>> = list.map { convert(it) } // through some logic, convert each object in the collection
val desired: Either<ConvertError, Collection<NewObject>> = eithers.map { ??? }
fun convert(o: Object) : Either<ConvertError, NewObject> { ... }
本质上,我想在数据集合上调用映射函数,如果任何映射响应失败,我想要一个包含错误的 Either.Left()
。然后,我希望 Either.Right()
包含所有映射的对象。
有什么干净的方法可以做到这一点吗?理想情况下,我想进行一系列函数调用,但能够通过函数调用向上渗透错误。
这是一个常见的,你要找的是traverse
。类似于map
,只是它按照内容的聚合规则收集结果。
因此,list.k().traverse(Either.applicative()) { convert(it) }
将 return Either.Left
是任何操作 return Left
,否则 Right<List<
。
您可以使用 Arrow 的计算块在 map
内展开 Either
,如下所示:
import arrow.core.Either
import arrow.core.computations.either
val list: ListObject> // some collection
val eithers: List<Either<ConvertError, NewObject>> = list.map { convert(it) } // through some logic, convert each object in the collection
val desired: Either<ConvertError, Collection<NewObject>> = either.eager {
eithers.map { convert(it).bind() }
}
fun convert(o: Object) : Either<ConvertError, NewObject> { ... }
此处 bind()
将在 Either
为 Right
的情况下将 Either
展开为 NewObject
,否则它将退出 either.eager
阻止以防它发现 Left
和 ConvertError
。这里我们使用 eager { }
变体,因为我们立即将其分配给 val
。主 suspend fun either { }
块在内部支持 suspend
函数,但它本身也是一个 suspend
函数。
这是 traverse
运算符的替代方法。
traverse
操作在 Arrow 0.12.0 中将简化为以下内容:
import arrow.core.traverseEither
eithers.traverseEither(::convert)
Arrow Fx Coroutines 中也提供了 traverse
运算符,支持并行遍历,以及该运算的一些强大的派生词。
import arrow.fx.coroutines.parTraverseEither
eithers.parTraverseEither(Dispatcheres.IO, ::convert)
arrow.core.IterableKt#sequenceEither
怎么样?
val desired: Either<ConvertError, Collection<NewObject>> = eithers.sequenceEither()
我最近一直在使用 kotlin arrow,我 运行 遇到了一个让我卡住的特定用例。
假设我有一些对象的集合,我想使用转换函数将其转换为另一种数据类型。我们还假设这个转换函数有失败的能力——但它不会抛出异常,它只会 return 一个 Either,其中 Either.Left()
是失败而 Either.Right()
是映射目的。处理此用例的最佳方法是什么?下面是一些示例代码:
val list: Collection<Object> // some collection
val eithers: List<Either<ConvertError, NewObject>> = list.map { convert(it) } // through some logic, convert each object in the collection
val desired: Either<ConvertError, Collection<NewObject>> = eithers.map { ??? }
fun convert(o: Object) : Either<ConvertError, NewObject> { ... }
本质上,我想在数据集合上调用映射函数,如果任何映射响应失败,我想要一个包含错误的 Either.Left()
。然后,我希望 Either.Right()
包含所有映射的对象。
有什么干净的方法可以做到这一点吗?理想情况下,我想进行一系列函数调用,但能够通过函数调用向上渗透错误。
这是一个常见的,你要找的是traverse
。类似于map
,只是它按照内容的聚合规则收集结果。
因此,list.k().traverse(Either.applicative()) { convert(it) }
将 return Either.Left
是任何操作 return Left
,否则 Right<List<
。
您可以使用 Arrow 的计算块在 map
内展开 Either
,如下所示:
import arrow.core.Either
import arrow.core.computations.either
val list: ListObject> // some collection
val eithers: List<Either<ConvertError, NewObject>> = list.map { convert(it) } // through some logic, convert each object in the collection
val desired: Either<ConvertError, Collection<NewObject>> = either.eager {
eithers.map { convert(it).bind() }
}
fun convert(o: Object) : Either<ConvertError, NewObject> { ... }
此处 bind()
将在 Either
为 Right
的情况下将 Either
展开为 NewObject
,否则它将退出 either.eager
阻止以防它发现 Left
和 ConvertError
。这里我们使用 eager { }
变体,因为我们立即将其分配给 val
。主 suspend fun either { }
块在内部支持 suspend
函数,但它本身也是一个 suspend
函数。
这是 traverse
运算符的替代方法。
traverse
操作在 Arrow 0.12.0 中将简化为以下内容:
import arrow.core.traverseEither
eithers.traverseEither(::convert)
Arrow Fx Coroutines 中也提供了 traverse
运算符,支持并行遍历,以及该运算的一些强大的派生词。
import arrow.fx.coroutines.parTraverseEither
eithers.parTraverseEither(Dispatcheres.IO, ::convert)
arrow.core.IterableKt#sequenceEither
怎么样?
val desired: Either<ConvertError, Collection<NewObject>> = eithers.sequenceEither()