Jak poradzić sobie z monadą "czytelnika" i "spróbować"?
Czytam świetny artykuł o dependency injection w Scali z Reader monad.
Oryginalny przykład działa dobrze, ale trochę zmieniłem typy zwrotów UserRepository.get/find
. To było User
, ale zmieniłem na Try[User]
.
Wtedy kod nie zostanie skompilowany, próbowałem wiele razy, ale nadal bez szczęścia.
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}"
)
}
Błąd kompilacji to:
Error:(34, 12) type mismatch;
found : scala.util.Try[Nothing]
required: scalaz.Kleisli[scalaz.Id.Id,?,?]
user <- userTry
^
I
Error:(36, 12) type mismatch;
found : scala.util.Try[scala.collection.immutable.Map[String,String]]
required: scalaz.Kleisli[scalaz.Id.Id,?,?]
boss <- bossTry
^
I read the document of Kleisli.flatMap
(the return type z findUser
i getUser
jest Kleisli
), wymaga typu parametru:
B => Kleisli[M, A, C]
Ponieważ Try
nie będzie Kleisli
, są takie błędy.
scala.util.Try
tutaj? Jak Mogę zmienić go na typ KLeisli
? Jak sprawić, by ten przykład działał? 1 answers
Możesz użyć transformatora ReaderT
monad, aby skomponować monadę Reader
i monadę Try
w jedną monadę, na której możesz użyć for
-zrozumienie itp.
ReaderT
jest tylko aliasem typu dla Kleisli
i możesz użyć Kleisli.kleisli
zamiast Reader.apply
do konstruowania obliczeń Reader
-y. Pamiętaj, że potrzebujesz scalaz-contrib
dla instancji monad dla Try
(lub możesz napisać własną-to dość proste).
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))
}
Teraz, gdy to się stało, UserInfo
jest znacznie prostsze (i to teraz też kompiluje!):
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}"
)
}
Możemy pokazać, że działa:
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"))
}
}
A potem:
UserInfo.userInfo("foo").run(repo).foreach(println)
Map(fullName -> Foo McBar, email -> [email protected], boss -> Bar McFoo)
Dokładnie w ten sam sposób, w jaki uruchomiłeś Reader
, ale dostajesz Try
na końcu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-20 17:12:55