在处理它之前,我们经常需要一些请求验证。使用箭头 v 0.8,典型的消息处理程序如下所示:
fun addToShoppingCart(request: AddToShoppingCartRequest): IO<Either<ShoppingCardError, ItemAddedEvent>> = fx {
request
.pipe (::validateShoppingCard)
.flatMap { validatedRequest ->
queryShoppingCart().bind().map { validatedRequest to it } // fun queryShoppingCart(): IO<Either<DatabaseError, ShoppingCart>>
}
.flatMap { (validatedRequest, shoppingCart) ->
maybeAddToShoppingCart(shoppingCart, validatedRequest) // fun maybeAddToShoppingCart(...): Either<DomainError, ShoppingCart>
}
.flatMap { updatedShoppingCart ->
storeShoppingCart(updatedShoppingCart).bind() // fun storeShoppingCart(ShoppingCart): IO<Either<DatabaseError, Unit>>
.map {
computeItemAddedEvent(updatedShoppingCart)
}
}
.mapLeft(::computeShoppingCartError)
}
这似乎是工作流的一个方便且富有表现力的定义。我试图在箭头 v 0.10.5 中定义类似的功能:
fun handleDownloadRequest(strUrl: String): IO<Either<BadUrl, MyObject>> = IO.fx {
parseUrl(strUrl) // fun(String): Either<BadUrl,Url>
.map {
!effect{ downloadObject(it) } // suspended fun downloadObject(Url): MyObject
}
}
这会导致编译器错误“只能在协程主体内调用暂停函数”。原因是既map
和flatMap
功能Either
又Option
不是inline
。
事实上,关于 fx 的博客文章说
“很快你会发现你不能在为 Either 声明的函数中调用挂起函数,例如上面提到的函数,以及其他粉丝喜欢的 map() 和 handleErrorWith()。为此,你需要一个并发库!”
所以问题是为什么会这样,这种组合的惯用方式是什么?