Ответ 1
Вы можете использовать монадный трансформатор ReaderT
для создания монады Reader
и монады Try
в одну монаду, в которой вы можете использовать for
-понимание и т.д.
ReaderT
- это просто псевдоним типа Kleisli
, и вы можете использовать Kleisli.kleisli
вместо Reader.apply
для создания ваших вычислений Reader
-y. Обратите внимание, что вам нужен scalaz-contrib
для экземпляра monad для Try
(или вы можете написать свой собственный - это довольно просто).
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("[email protected]", 0, "Bar", "McFoo")
val foo = User("[email protected]", 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 -> [email protected], boss -> Bar McFoo)
Точно так же вы запускаете Reader
, но в конце вы получите Try
.