【问题标题】:How to handle `Reader` monad and `Try`?如何处理`Reader` monad 和`Try`?
【发布时间】:2014-11-14 23:23:29
【问题描述】:

我正在阅读这篇关于 dependency injection in scala with Reader monad 的精彩文章。

原始示例运行良好,但我对UserRepository.get/find 的返回类型做了一些更改。原来是User,但我改成了Try[User]。

那代码就编译不出来了,我试了很多次,还是没有运气。

import scala.util.Try
import scalaz.Reader

case class User(email: String, supervisorId: Int, firstName: String, lastName: String)

trait UserRepository {
  def get(id: Int): Try[User]

  def find(username: String): Try[User]
}

trait Users {

  def getUser(id: Int) = Reader((userRepository: UserRepository) =>
    userRepository.get(id)
  )

  def findUser(username: String) = Reader((userRepository: UserRepository) =>
    userRepository.find(username)
  )
}

object UserInfo extends Users {

  def userEmail(id: Int) = {
    getUser(id) map (ut => ut.map(_.email))
  }

  def userInfo(username: String) =
    for {
      userTry <- findUser(username)
      user <- userTry     // !!!!!!!! compilation error
      bossTry <- getUser(user.supervisorId)
      boss <- bossTry     // !!!!!!!! compilation error
    } yield Map(
      "fullName" -> s"${user.firstName} ${user.lastName}",
      "email" -> s"${user.email}",
      "boss" -> s"${boss.firstName} ${boss.lastName}"
    )
}

编译错误是:

Error:(34, 12) type mismatch;
 found   : scala.util.Try[Nothing]
 required: scalaz.Kleisli[scalaz.Id.Id,?,?]
      user <- userTry
           ^

和

Error:(36, 12) type mismatch;
 found   : scala.util.Try[scala.collection.immutable.Map[String,String]]
 required: scalaz.Kleisli[scalaz.Id.Id,?,?]
      boss <- bossTry
           ^

我看了Kleisli.flatMap的文档(findUser和getUser的返回类型是Kleisli),它要求的参数类型是:

B => Kleisli[M, A, C]

因为Try 不会是Kleisli,所以会有这样的错误。

我不知道如何处理它。我可以在这里使用scala.util.Try 吗?我怎样才能把它变成KLeisli 类型?我怎样才能使这个例子工作?

【问题讨论】:

    标签: scala dependency-injection monads scalaz


    【解决方案1】:

    您可以使用ReaderT monad 转换器将Reader monad 和Try monad 组合成一个monad,您可以使用for-comprehension 等。

    ReaderT 只是Kleisli 的类型别名,您可以使用Kleisli.kleisli 而不是Reader.apply 来构造Reader-y 计算。请注意,Try 的 monad 实例需要 scalaz-contrib(或者您可以自己编写——这很简单)。

    import scala.util.Try
    import scalaz._, Scalaz._
    import scalaz.contrib.std.utilTry._
    
    case class User(
      email: String,
      supervisorId: Int,
      firstName: String,
      lastName: String
    )
    
    trait UserRepository {
      def get(id: Int): Try[User]
    
      def find(username: String): Try[User]
    }
    
    trait Users {
      def getUser(id: Int): ReaderT[Try, UserRepository, User] =
        Kleisli.kleisli(_.get(id))
    
      def findUser(username: String): ReaderT[Try, UserRepository, User] =
        Kleisli.kleisli(_.find(username))
    }
    

    既然已经完成了,UserInfo 就简单多了(现在也可以编译了!):

    object UserInfo extends Users {
      def userEmail(id: Int) = getUser(id).map(_.email)
    
      def userInfo(
        username: String
      ): ReaderT[Try, UserRepository, Map[String, String]] =
        for {
          user <- findUser(username)
          boss <- getUser(user.supervisorId)
        } yield Map(
          "fullName" -> s"${user.firstName} ${user.lastName}",
          "email" -> s"${user.email}",
          "boss" -> s"${boss.firstName} ${boss.lastName}"
        )
    }
    

    我们可以证明它有效:

    import scala.util.{ Failure, Success }
    
    val repo = new UserRepository {
      val bar = User("bar@mcfoo.com", 0, "Bar", "McFoo")
      val foo = User("foo@mcbar.com", 0, "Foo", "McBar")
    
      def get(id: Int) = id match {
        case 0 => Success(bar)
        case 1 => Success(foo)
        case i => Failure(new Exception(s"No user with id $i"))
      }
    
      def find(username: String) = username match {
        case "bar" => Success(bar)
        case "foo" => Success(foo)
        case other => Failure(new Exception(s"No user with name $other"))
      }
    }
    

    然后:

    UserInfo.userInfo("foo").run(repo).foreach(println)
    Map(fullName -> Foo McBar, email -> foo@mcbar.com, boss -> Bar McFoo)
    

    与运行Reader 的方式完全相同,但最后会得到Try。

    【讨论】:

      猜你喜欢
      • 2018-03-26
      • 1970-01-01
      • 2017-06-22
      • 2023-04-05
      • 2019-04-23
      • 2014-01-16
      • 2021-01-18
      • 2016-10-17
      • 2017-08-16
      相关资源
      最近更新 更多