0

我在我的 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)
}

但现在我正在努力使用它。我不明白我们怎么能说FinuserRepository必须是一个 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实例实现存储库以及如何在服务类中使用此类存储库?

4

1 回答 1

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

干杯!

于 2019-09-21T01:19:51.847 回答