我在Kotlin后端项目中使用Arrow。我有这样的存储库:
interface UserRepository {
fun user(username: String): Try<Option<User>>
}
现在,我想更进一步,从具体的Try
类型抽象出来,而返回Kind<F, Option<User>>
。我可以用以下代码做到这一点:
interface UserRepository<F> {
fun user(username: String): Kind<F, Option<User>>
}
class IdRepository : UserRepository<ForId> {
fun user(username: String): Kind<ForId<Option<User>>> =
if (username == "known") Id.just(Some(User()))
else Id.just(None)
}
但是现在我正在努力使用它。我不明白如何说F
中的userRepository
必须是Monad,以便可以在monad理解块中使用它。假设我有一些这样定义的类:
class UserService<F>(ME: MonadError<F, Throwable>, repo: UserRepository<F>)
: MonadError<F, Throwable> by ME {
fun someOperations(username: String) : Kind<F, User> = bindingCatch {
val (user) = repo.user(username)
user.fold({ /* create user */ }, { /* return user */ })
}
}
编译器抱怨无法在行user
上绑定repo.user
,因为它需要Kind<ForTry, ...>
,但是repo.user
返回Kind<F, ...>
,这在这里是未知的。如何正确地从Try
实现抽象,以便我可以使用Id
实例实现存储库,以及如何在服务类中使用此类存储库?
答案 0 :(得分:1)
在0.10.0中,可以使用Fx类型类执行monad绑定。如您的示例中的kdoc中所述,可以使用它的变体,其中每个变体都表示所需的功率级别。实际上,大多数应用都使用IO.fx
,因为效果只能完全封装在IO
中。如果要处理副作用,则只能替换运行时,只要它们支持挂起即可,这样基本上可以将运行时选项的范围缩小到Async<F>
实例,因为挂起意味着潜在的异步工作。那是IO,Rx等...但是从不尝试,...这些都非常适合渴望高效的纯计算
/**
* Fx allows you to run pure sequential code as if it was imperative.
*
* @see [arrow.typeclasses.suspended.monad.Fx] // Anything with flatMap
* @see [arrow.typeclasses.suspended.monaderror.Fx] //Try, Either etc stop here
* @see [arrow.fx.typeclasses.suspended.monaddefer.Fx] // IO
* @see [arrow.fx.typeclasses.suspended.concurrent.Fx] // IO
*/
class UserService<F>(ME: MonadError<F, Throwable>, repo: UserRepository<F>)
: MonadError<F, Throwable> by ME {
fun someOperations(username: String) : Kind<F, User> =
fx.monadThrow {
val user = !repo.user(username)
user.fold({ /* create user */ }, { /* return user */ })
}
}
}
如果您希望通过https://slack.kotlinlang.org #arrow频道获得更详细的解释,我们将很乐意为您提供帮助,并且可以在Kotlin上讨论FP
干杯!