Kotlin arrow-kt,将一个集合映射到一个集合的功能方法

问题描述

我最近一直在使用 kotlin arrow,但遇到了一个让我陷入困境的特定用例。

假设我有一些对象的集合,我想使用转换函数将其转换为另一种数据类型。假设这个转换函数有失败的能力——但它不会抛出异常,它只会返回一个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) } 将返回 Either.Left 是任何返回 Left 的操作,否则返回 Right<List<

,

您可以像这样使用 Arrow 的计算块将 Either 解包到 map 中:

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() 将在 EitherNewObject 的情况下将 Either 解包为 Right,或者它将退出 either.eager 块如果它找到 LeftConvertError。此处我们使用 eager { } 变体,因为我们立即将其分配给 val。主 suspend fun either { } 块在内部支持 suspend 函数,但它本身也是一个 suspend 函数。

这是 traverse 运算符的替代方法。 traverse 操作将在 Arrow 0.12.0 中简化为以下内容:

import arrow.core.traverseEither

eithers.traverseEither(::convert) 

Arrow Fx 协程中也提供了 traverse 运算符,支持并行遍历,以及此操作的一些强大派生。

import arrow.fx.coroutines.parTraverseEither

eithers.parTraverseEither(Dispatcheres.IO,::convert)