如何从 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

干杯!