如何使用monad和避免嵌套的flatMap



我在一个情况下,我试图设置一些数据,然后调用一个服务。每一步都可能失败,所以我试图使用箭头的要么来管理这个。

但是我最终得到了很多嵌套的flatMaps。

下面的代码片段说明了我要做的事情:

import arrow.core.Either
import arrow.core.flatMap
typealias ErrorResponse = String
typealias SuccessResponse = String
data class Foo(val userId: Int, val orderId: Int, val otherField: String)
data class User(val userId: Int, val username: String)
data class Order(val orderId: Int, val otherField: String)
interface MyService {
fun doSomething(foo: Foo, user: User, order: Order): Either<ErrorResponse, SuccessResponse> {
return Either.Right("ok")
}
}
fun parseJson(raw: String): Either<ErrorResponse, Foo> = TODO()
fun lookupUser(userId: Int): Either<ErrorResponse, User> = TODO()
fun lookupOrder(orderId: Int): Either<ErrorResponse, Order> = TODO()
fun start(rawData: String, myService: MyService): Either<ErrorResponse, SuccessResponse> {
val foo = parseJson(rawData)
val user = foo.flatMap {
lookupUser(it.userId)
}
//I want to lookupOrder only when foo and lookupUser are successful
val order = user.flatMap {
foo.flatMap { lookupOrder(it.orderId) }
}
//Only when all 3 are successful, call the service
return foo.flatMap { f ->
user.flatMap { u ->
order.flatMap { o ->
myService.doSomething(f, u, o)
}
}
}
}

我肯定有更好的方法来做这件事。有人能教我一个习惯用语吗?

您可以使用either { }DSL,这可以通过suspend方式或通过either.eager { }构建器以非挂起方式获得。

这样你就可以使用suspend fun <E, A> Either<E, A>.bind(): A了。

重写代码示例:

fun start(rawData: String, myService: MyService): Either<ErrorResponse, SuccessResponse> =
either.eager {
val foo = parseJson(rawData).bind()
val user =  lookupUser(foo.userId).bind()
val order = lookupOrder(foo.orderId).bind()
myService.doSomething(foo, user, order).bind()
}

如果遇到Either.Left,则bind()短路either.eager块并返回遇到的Either.Left值。

最新更新