在Scala / Play中编写多个Try对象

时间:2013-11-24 03:12:01

标签: scala playframework

我们有一个Scala / Play应用程序,其中有几个隐式类可以从Request创建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从Try

生成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))
}

但这会导致案例类+隐式类定义组合爆炸。理想情况下,我希望能够以临时方式组合多个Try对象,例如

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

并为我做了一个神奇的尝试[用户分页],但我不知道我是怎么做的 - 我一直在努力尝试为类型系统分配一个有意义的类型来“编写” “没有任何成功。

如何将多个Try对象组合在一起,或者使用另一种语言构造等效?

1 个答案:

答案 0 :(得分: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)
    } 
}