如何处理`Reader` monad和`Try`? [英] How to handle `Reader` monad and `Try`?

查看:106
本文介绍了如何处理`Reader` monad和`Try`?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读有关 scala中的依赖注入的伟大文章与读者monad

原始示例运行良好,但是对 UserRepository.get / find 。这是用户,但我将其更改为尝试[用户]

The original example is working well, but I did a little bit change on the return types of the UserRepository.get/find. It was User, but I changed it to Try[User].

然后代码不会被编译,我尝试了很多次,但还是没有幸运。

Then the code won't be compiled, I had tries many times, but still without lucky.

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 ),它需要参数类型为:

I read the document of Kleisli.flatMap (The return type of findUser and getUser is Kleisli), it requires the parameter type is:

B => Kleisli[M, A, C]

由于尝试不会是一个 Kleisli ,有这样的错误。

Since a Try won't be a Kleisli, there are such errors.

我不知道如何处理它。我可以在这里使用 scala.util.Try 吗?如何将其转换为 KLeisli 类型?如何使这个例子工作?

I'm not sure how to handle it. Can I use scala.util.Try here? How can I turn it to a KLeisli type? How can I make this example work?

推荐答案

你可以使用 ReaderT monad变压器组成读者 monad和尝试 monad到一个monad,你可以使用 for -comprehension on,etc。

You can use the ReaderT monad transformer to compose the Reader monad and the Try monad into a single monad that you can use a for-comprehension on, etc.

ReaderT 只是一种类型 Kleisli 的别名,您可以使用 Kleisli.kleisli 而不是 Reader.apply 构建您的读者 - 计算。请注意,您需要 scalaz-contrib for the monad instance for 尝试(或者你可以自己写 - 这很简单)。

ReaderT is just a type alias for Kleisli, and you can use Kleisli.kleisli instead of Reader.apply to construct your Reader-y computations. Note that you need scalaz-contrib for the monad instance for Try (or you can write your own—it's pretty straightforward).

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 简单得多(现在也编译了):

Now that that's done, UserInfo is much simpler (and it compiles now, too!):

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}"
    )
}

我们可以显示它的作品:

We can show it works:

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)

完全一样的方式你会运行读者,但你得到一个尝试结束。

Exactly the same way you'd run a Reader, but you get a Try at the end.

这篇关于如何处理`Reader` monad和`Try`?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆