我最近一直在使用 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()
包含所有映射对象。
有什么干净的方法可以做到这一点吗?理想情况下,我希望进行一系列函数调用,但能够通过函数调用向上渗透错误。
答案 0 :(得分:3)
这是常见的,您要查找的内容称为 traverse
。类似于map
,只不过它按照内容的聚合规则来收集结果。
因此,list.k().traverse(Either.applicative()) { convert(it) }
将返回 Either.Left
是任何返回 Left
的操作,否则返回 Right<List<
。
答案 1 :(得分:2)
您可以像这样使用 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()
将在 Either
为 NewObject
的情况下将 Either
解包为 Right
,或者它将退出 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 协程中也提供了 traverse
运算符,支持并行遍历,以及此操作的一些强大派生。
import arrow.fx.coroutines.parTraverseEither
eithers.parTraverseEither(Dispatcheres.IO, ::convert)