如何处理`Reader` monad和`Try`? [英] How to handle `Reader` monad and `Try`?
问题描述
我正在阅读有关 scala中的依赖注入的伟大文章与读者monad 。
原始示例运行良好,但是对 UserRepository.get / find $ c的返回类型进行了一些更改$ C>。这是
用户
,但我将其更改为尝试[用户]
。
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屋!