意外的scala.MatchError

时间:2015-07-26 17:41:30

标签: scala playframework

我有一个方法,其定义如下:

def userPosts(userId: String): Future[List[Post]]

如果没有缓存数据,我希望有一个返回缓存日期的方法或调用userPosts。我已经准备好了这样的事情:

def cachedUserPosts(userId: String): Future[List[Post]] = {
    for {
        cached <- bucket.get[List[Post]]("posts_" + userId)
    } yield {
        cached match {
            case Some(list) => list
            case None => userPosts(userId) match {
                 case list:List[Post] => list
            }
        }
    }
}

我有一个错误:

[error] play - Cannot invoke the action, eventually got an error: scala.MatchError: scala.concurrent.impl.Promise$DefaultPromise@23cf4a0c (of class scala.concurrent.impl.Promise$DefaultPromise)

play.api.Application$$anon$1: Execution exception[[MatchError: scala.concurrent.impl.Promise$DefaultPromise@23cf4a0c (of class scala.concurrent.impl.Promise$DefaultPromise)]]
    at play.api.Application$class.handleError(Application.scala:296) ~[play_2.11-2.3.6.jar:2.3.6]
    at play.api.DefaultApplication.handleError(Application.scala:402) [play_2.11-2.3.6.jar:2.3.6]
    at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$3$$anonfun$applyOrElse$4.apply(PlayDefaultUpstreamHandler.scala:320) [play_2.11-2.3.6.jar:2.3.6]
    at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$3$$anonfun$applyOrElse$4.apply(PlayDefaultUpstreamHandler.scala:320) [play_2.11-2.3.6.jar:2.3.6]
    at scala.Option.map(Option.scala:145) [scala-library-2.11.2.jar:na]
Caused by: scala.MatchError: scala.concurrent.impl.Promise$DefaultPromise@23cf4a0c (of class scala.concurrent.impl.Promise$DefaultPromise)
    at models.Post$$anonfun$fastUserPosts$1.apply(Post.scala:130) ~[classes/:na]
    at models.Post$$anonfun$fastUserPosts$1.apply(Post.scala:126) ~[classes/:na]
    at scala.util.Success$$anonfun$map$1.apply(Try.scala:236) ~[scala-library-2.11.2.jar:na]
    at scala.util.Try$.apply(Try.scala:191) ~[scala-library-2.11.2.jar:na]
    at scala.util.Success.map(Try.scala:236) ~[scala-library-2.11.2.jar:na]
2015-07-26 19:33:24.211 INFO net.spy.memcached.auth.AuthThread:  Authenticated to 192.168.10.42/192.168.10.42:11210

你知道发生了什么吗?

1 个答案:

答案 0 :(得分:1)

如果我们在控制台中尝试类似模式匹配,那么您在Future上进行模式匹配:

val p = Promise[Int]
val f = p.future
p.success(5) // fulfill the promise

f上的模式匹配为我们提供了:

scala> f match { case i: Int => i }
<console>:15: error: pattern type is incompatible with expected type;
 found   : Int
 required: scala.concurrent.Future[Int]
              f match { case i: Int => i }
                                ^

您的userPosts函数返回Future[List[Post]],您无法直接模式匹配以获得List[Post]。您可以在Future上找到有关使用Scala website的更多信息。

让我们修复您的cachedUserPosts功能。您的版本也可以写成(不编译):

bucket.get[List[Post]]("posts_" + userId) map ( cached => cached match {
  // List[Post] in Future.map => Future[List[Post]]
  case Some(list) => list
  // userPosts already gives us a Future[List[Post]]
  // We could have used Await.result(userPosts(userId), 5.seconds)
  // to get the List[Post] from the Future, but then we would be blocking
  case None => userPosts(userId)
})

如果我们将map转换为flatMap,我们可以返回Future[List[Post]],但我们还需要在Future[List[Post]]中返回Some case,可以使用Future.successful轻松完成:

bucket.get[List[Post]]("posts_" + userId) flatMap ( cached => cached match {
  // List[Post] to Future[List[Post]] using Future.successful
  case Some(list) => Future.successful(list)
  case None => userPosts(userId)
})

我们可以使用map / getOrElse代替Option上的模式匹配来清理这一点:

def cachedUserPosts(userId: String): Future[List[Post]] =
  bucket.get[List[Post]]("posts_" + userId).flatMap { cached =>
     cached.map(list => Future.successful(list))
           .getOrElse(userPosts(userId))
  }