Fre*_*ind 11 monads dependency-injection scala scalaz
我正在阅读这篇关于使用Reader monad进行scala依赖注入的精彩文章.
原始示例运行良好,但我对返回类型做了一点改动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}"
)
}
Run Code Online (Sandbox Code Playgroud)
编译错误是:
Error:(34, 12) type mismatch;
found : scala.util.Try[Nothing]
required: scalaz.Kleisli[scalaz.Id.Id,?,?]
user <- userTry
^
Run Code Online (Sandbox Code Playgroud)
和
Error:(36, 12) type mismatch;
found : scala.util.Try[scala.collection.immutable.Map[String,String]]
required: scalaz.Kleisli[scalaz.Id.Id,?,?]
boss <- bossTry
^
Run Code Online (Sandbox Code Playgroud)
我读了文件Kleisli.flatMap(返回类型findUser和getUser是Kleisli),它要求参数类型为:
B => Kleisli[M, A, C]
Run Code Online (Sandbox Code Playgroud)
由于a Try不会是a Kleisli,所以会出现这样的错误.
我不知道如何处理它.我可以scala.util.Try在这里用吗?我怎样才能把它变成一个KLeisli类型?我怎样才能使这个例子有效?
Tra*_*own 20
你可以使用ReaderTmonad变换器将Readermonad和Trymonad组合成一个monad,你可以使用a for-reherehe on等.
ReaderT只是一个类型别名Kleisli,您可以使用Kleisli.kleisli而不是Reader.apply构造您的Reader-y计算.请注意,您需要scalaz-contribmonad实例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))
}
Run Code Online (Sandbox Code Playgroud)
既然已经完成了,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}"
)
}
Run Code Online (Sandbox Code Playgroud)
我们可以证明它有效:
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"))
}
}
Run Code Online (Sandbox Code Playgroud)
然后:
UserInfo.userInfo("foo").run(repo).foreach(println)
Map(fullName -> Foo McBar, email -> foo@mcbar.com, boss -> Bar McFoo)
Run Code Online (Sandbox Code Playgroud)
你运行的方式完全相同Reader,但Try最终会得到一个.
| 归档时间: |
|
| 查看次数: |
1056 次 |
| 最近记录: |