cats.ReaderT[F,A,B] 来自依赖 A 的理解

cats.ReaderT[F,A,B] from dependency A in for-comprehension

这是一个用例:

import cats.data.ReaderT
trait Service{
  type OptionFromMap[A] = ReaderT[Option, Map[String, String], A]
  def f1(nameKey:String): OptionFromMap[String] = ReaderT(_.get(nameKey))
  def f2(addressKey:String, name:String): OptionFromMap[String] =
    ReaderT(map => Option(s"name: $name, address: ${map(addressKey)}"))
}
trait Service2 {
  type Env = (Service, Map[String,String])
  type OptionFromEnv[A] = ReaderT[Option, Env, A]
  import cats.syntax.applicative._
  import cats.instances.option._
  def f(nameKey:String, addressKey:String): OptionFromEnv[String] =
    for {
      //wrong try:
      s1 <- ReaderT((_:Env) => Option((_:Env)._1))
      //wrong try:
      s2 <- (_:Env).pure[OptionFromEnv]
      name <- s1.f1(nameKey).local((_: Env)._2)
      r <- s1.f2(addressKey, name).local((_: Env)._2)
    } yield r
}

所以我希望能够调用 Servicef1f2 方法。

问题是如何在for-comprehension里面做。通过 ReaderT.apply,我可以通过以下方式完成它:

  def c(nameKey:String, addressKey:String): OptionFromEnv[String] =
    ReaderT(env =>
      (for {
        name <- env._1.f1(nameKey).local((_: Env)._2)
        r <- env._1.f2(addressKey, name).local((_: Env)._2)
      } yield r)
      .run(env)
    )

但我试图实现一些东西,看起来像:

import cats.data.Reader
trait Service{
  def f1: Reader[Map[String, Int], Int] = Reader(_("name"))
  def f2: Reader[Map[String, Int], Int] = Reader(_("age"))
}
trait Service2 {
  type Env = (Service, Map[String,Int])
  def f(i: Int): Reader[Env, Int] =
    for {
      s <- Reader((_: Env)._1)          //extract input type Service 
      r1 <- s.f1.local((_: Env)._2)
      r2 <- s.f2.local((_: Env)._2)
    } yield r1 + r2 + i
}

尝试替换

//wrong try:
s1 <- ReaderT((_:Env) => Option((_:Env)._1))

s1 <- ReaderT((env: Env) => Option(env._1))

通过 Kleisli.ask 找到了解决问题的其他方法。不能说哪个选项更好。作为替代方案:

trait Service2 {

  def f(nameKey:String, addressKey:String): OptionFromEnv[String] =
    for {
      env <- Kleisli.ask[Option, Env]
      s <- Kleisli.pure(env._1)
      name <- env._1.f1(nameKey).local((_: Env)._2)
      r <- s.f2(addressKey, name).local((_: Env)._2)
    } yield r
}

一般来说,我们有办法获取env里面的for-comprehension。当然,也没有必要从 env.

得到 s