如何从 arrow-kt 中的 Try 中抽象出来
How to abstract from Try in arrow-kt
我在我的 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)
}
但现在我正在努力使用它。我不明白我们怎么能说 userRepository
中的 F
必须是 Monad,以便它可以在 monad 理解块中使用。假设我有一些 class 定义如下:
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 */ })
}
}
编译器抱怨它无法在行 repo.user
上绑定 user
,因为它需要 Kind<ForTry, ...>
但 repo.user
returns Kind<F, ...>
这在这里是未知的。如何从 Try
中正确实现抽象以便我可以使用 Id
实例实现存储库以及如何在服务 classes?
中使用此类存储库
在 0.10.0 中你可以使用 Fx 类型 class 来执行 monad 绑定。它的变体如您的示例中的 kdoc 中所述可用,其中每个变体都代表您想要的功率级别。实际上,大多数应用程序都使用 IO.fx
,因为效果只能纯粹封装在 IO
中。如果您正在处理副作用,您只能替换支持挂起的运行时,因此这基本上将您的运行时选项缩小到 Async<F>
的实例,因为挂起意味着潜在的异步工作。那是 IO、Rx 等...但永远不要尝试、Either...这些对于渴望无效的纯计算很有用
/**
* 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
干杯!
我在我的 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)
}
但现在我正在努力使用它。我不明白我们怎么能说 userRepository
中的 F
必须是 Monad,以便它可以在 monad 理解块中使用。假设我有一些 class 定义如下:
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 */ })
}
}
编译器抱怨它无法在行 repo.user
上绑定 user
,因为它需要 Kind<ForTry, ...>
但 repo.user
returns Kind<F, ...>
这在这里是未知的。如何从 Try
中正确实现抽象以便我可以使用 Id
实例实现存储库以及如何在服务 classes?
在 0.10.0 中你可以使用 Fx 类型 class 来执行 monad 绑定。它的变体如您的示例中的 kdoc 中所述可用,其中每个变体都代表您想要的功率级别。实际上,大多数应用程序都使用 IO.fx
,因为效果只能纯粹封装在 IO
中。如果您正在处理副作用,您只能替换支持挂起的运行时,因此这基本上将您的运行时选项缩小到 Async<F>
的实例,因为挂起意味着潜在的异步工作。那是 IO、Rx 等...但永远不要尝试、Either...这些对于渴望无效的纯计算很有用
/**
* 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
干杯!