2014-09-20 3 views
11

मैं इस महान लेख को dependency injection in scala with Reader monad के बारे में पढ़ रहा हूं।'रीडर' मोनैड और 'कोशिश करें' को कैसे संभालें?

मूल उदाहरण अच्छी तरह से काम कर रहा है, लेकिन मैंने 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}" 
    ) 
} 

संकलन त्रुटि है:

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 की वापसी प्रकार और getUserKleisli है) पढ़ें, यह पैरामीटर प्रकार की आवश्यकता है है:

B => Kleisli[M, A, C] 

चूंकि TryKleisli नहीं होगा, ऐसी त्रुटियां हैं।

मुझे यकीन नहीं है कि इसे कैसे संभालें। क्या मैं यहां scala.util.Try का उपयोग कर सकता हूं? मैं इसे KLeisli प्रकार में कैसे बदल सकता हूं? मैं इस उदाहरण को कैसे काम कर सकता हूं?

उत्तर

20

आप Reader इकाई और एक एकल इकाई में Try इकाई है कि तुम पर एक for -comprehension उपयोग कर सकते हैं, आदि

ReaderTKleisli के लिए सिर्फ एक प्रकार अन्य नाम है रचना करने ReaderT इकाई ट्रांसफार्मर का उपयोग कर सकते हैं, और आप Reader -y computations बनाने के लिए Reader.apply के बजाय Kleisli.kleisli का उपयोग कर सकते हैं। ध्यान दें कि Try के लिए आपको मोनाड उदाहरण के लिए scalaz-contrib की आवश्यकता है (या आप अपना खुद का लिख ​​सकते हैं-यह बहुत सरल है)।

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 बहुत सरल है (और यह अब भी संकलित!):

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

और फिर:

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

हम यह काम करता है दिखा सकते हैं

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

बिल्कुल टी उसी तरह आप Reader चलाएंगे, लेकिन आपको अंत में Try मिल जाएगा।

संबंधित मुद्दे