2014-09-20 19 views
11

dependency injection in scala with Reader monad ile ilgili bu harika makaleyi okuyorum.Okuyucu monad ve `Try` nasıl kullanılır?

Orijinal örnek iyi çalışıyor, ancak UserRepository.get/find'un dönüş türlerinde biraz değişiklik yaptım. User idi, ancak Try[User] olarak değiştirdim. Daha sonra kod derlenmeyecek, defalarca denedim, ancak yine de şanssız.

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

derleme hatadır:

Error:(34, 12) type mismatch; 
found : scala.util.Try[Nothing] 
required: scalaz.Kleisli[scalaz.Id.Id,?,?] 
     user <- userTry 
     ^

ve

Error:(36, 12) type mismatch; 
found : scala.util.Try[scala.collection.immutable.Map[String,String]] 
required: scalaz.Kleisli[scalaz.Id.Id,?,?] 
     boss <- bossTry 
     ^
Ben Kleisli.flatMap ait belge ( findUser dönüş türünü ve getUser Kleisli olan) okumak

, bu parametre türünü gerektirir :

B => Kleisli[M, A, C] 

Try bir Kleisli olmayacağından, böyle hatalar vardır.

Nasıl yapılacağından emin değilim. scala.util.Try'u buradan kullanabilir miyim? Bir KLeisli türüne nasıl dönüştürebilirim? Bu örnek çalışmayı nasıl yapabilirim?

cevap

20

Sen

ReaderTKleisli için sadece bir tip takma vb Reader monad ve bir for -comprehension kullanabileceğiniz tek bir monad içine Try monad, oluşturmak için ReaderT monad trafo kullanabilirsiniz ve Reader -y hesaplarınızı oluşturmak için Reader.apply yerine Kleisli.kleisli'u kullanabilirsiniz. Try için monad örneği için scalaz-contrib'a ihtiyacınız olduğunu unutmayın (ya da kendiniz yazabilirsiniz — oldukça basittir). Şimdi Bu yapıldıktan sonra

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 çok daha basit (ve çok, şimdi derler!): Sonra

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

Ve:

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

Biz çalıştığını gösterebilir

UserInfo.userInfo("foo").run(repo).foreach(println) 
Map(fullName -> Foo McBar, email -> [email protected], boss -> Bar McFoo) 

Tam t Aynı şekilde bir Reader çalıştırırsınız, ancak sonunda bir Try alırsınız.