2013-11-24 13 views
2

我們有一個Scala/Play應用程序,其中有幾個隱式類可以根據請求創建Try對象,例如,在Scala/Play中編寫多個Try對象

implicit class RequestUtils[+T](req: Request[T]) { 
    def user: Try[User] = // pull the User from the Session, or throw an UnauthorizedException 
    def paging: Try[Paging] = // create a Paging object, or throw an IllegalArgumentException 
} 

然後,我們通過flatMaps

def route(pathParam: String) = BasicAction { 
    request => 
    request.user.flatMap(user => 
     request.paging.flatMap(paging => 
     Try{ ... } 
))} 

最後,一個ActionBuilder生成訪問包裝的對象從SimpleResult的嘗試

case class BasicRequest[A](request: Request[A]) extends WrappedRequest(request) 

class BasicActionBuilder extends ActionBuilder[BasicRequest] { 
    def invokeBlock[A](request: Request[A], block: (BasicRequest[A]) => Future[SimpleResult]) = { 
    block(BasicRequest(request)) 
    } 
} 

def BasicAction[T](block: BasicRequest[AnyContent] => Try[T]) = { 
    val f: BasicRequest[AnyContent] => SimpleResult = (req: BasicRequest[AnyContent]) => 
    block(req) match { 
     case Success(s) => Ok(convertToJson(s)) 
     case Failure(e: UnauthorizedException) => Unauthorized(e.getMessage) 
     case Failure(e: Exception) => BadRequest(e.getMessage) 
     case Failure(t: Throwable) => InternalServerError(e.getMessage) 
    } 

    val ab = new BasicActionBuilder 
    ab.apply(f) 
} 

我們試圖找到一種方法,實質上將多個Try對象組合在一起(或者沿着這些線條 - 我們並不喜歡使用Trys) - flatMaps對於一兩個Trys工作正常,但是將它們嵌套在阻礙程序之上可讀性。我們可以一起手動組合這些對象,例如

case class UserAndPaging(user: User, paging: Paging) 

implicit class UserAndPagingUtils[+T](req: Request[T]) { 
    def userAndPaging: Try[UserAndPaging] = req.user.flatMap(user => req.paging.flatMap(paging => UserAndPaging(user, paging)) 
} 

但是這會導致case class + implicit class def組合的爆炸。理想情況下,我希望能夠以特別的方式組合多個Try對象,例如

def route(pathParam: String) = BasicAction { 
    request => compose(request.user, request.paging).flatMap(userWithPaging => ...) 
} 

,並有一個嘗試[用戶與尋呼]神奇組成的我,但我不知道我怎麼會去這樣做 - 我一直在與類型系統摔跤嘗試分配一個有意義的鍵入「撰寫」沒有任何成功。

如何將多個Try對象組合在一起,或者使用另一種語言構造來構建等價物?

回答

6

Try可以均爲used in for-comprehensions,因爲他們有一個flatMap功能:

def route(pathParam: String) = BasicAction { request => 
    val userWithPaging = 
    for { 
     user <- request.user 
     paging <- request.paging 
    } yield { 
     doSomethingWith(user, paging) 
    } 
} 
+2

...不要忘記,'未來[T]'本質上是一個'試[T]'是不是招尚未完成 - 我們決定讓所有服務級別的電話都返回期貨,這很好。你可以獲得所有這些「完美」的好處,再加上它與Play的異步支持相得益彰。即使一個特定的服務並不需要異步,將結果包裝在Future.successful()中也是如此簡單,你也可以! :-) – millhouse