简体   繁体   English

Scala方法/播放框架/其余部分中的检查错误

[英]Inspection error in scala method / play framework / rest

I'm still learning scala so this might be a question with an easy answer, but I've been stuck on writing a single method over and over for almost a day, unable to get this code to compile. 我仍在学习scala,所以这可能是一个简单的答案,但是我一直在反复写一个方法长达近一天的时间,无法编译此代码。

I'm playing with the Play Framework and a reactive mongo template to learn how Scala and Play work. 我正在玩Play框架和反应性mongo模板,以了解Scala和Play的工作方式。 I have a controller with a few methods, endpoints for a REST service. 我有一个带有一些方法的控制器,一个REST服务的端点。

The issue is about the following method, which accepts a list of json objects and updates those objects using the mongo reactive driver. 问题与以下方法有关,该方法接受json对象列表并使用mongo反应性驱动程序更新这些对象。 The class has one member, citiesFuture which is of type Future[JSONCollection] . 该类具有一个citiesFuture成员,该成员的类型为Future[JSONCollection] The original class code which I'm adding this method to can be found here for context: CityController on github 我要添加此方法的原始类代码可以在上下文中找到: github上的CityController

def updateAll() = Action.async(parse.json) { request =>
  Json.fromJson[List[City]](request.body) match {
    case JsSuccess(givenCities, _) =>
      citiesFuture onComplete[Future[Result]] { cities =>
        val updateFutures: List[Future[UpdateWriteResult]] = for {
          city <- givenCities
        } yield cities.get.update(City.getUniqueQuery(city), Json.obj("$set" -> city))

        val promise: Promise[Result] = Promise[Result] {
          Future.sequence(updateFutures) onComplete[Result] {
            case s@Success(_) =>
              var count = 0
              for {
                updateWriteResult <- s.value
              } yield count += updateWriteResult.n
              promise success Ok(s"Updated $count cities")
            case Failure(_) =>
              promise success InternalServerError("Error updating cities")
          }
        }
        promise.future
      }
    case JsError(errors) =>
      Future.successful(BadRequest("Could not build a city from the json provided. " + Errors.show(errors)))
  }
}

I've managed to get this far with alot of trial and error, but I'm starting to understand how some of the mechanics of scala and Futures work, I think :) I think I'm close, but my IDE still gives me a single Inspection error just at the single closing curly brace above the line promise.future . 我已经通过大量的尝试和错误努力做到了这一点,但是我开始理解一些scala和Futures的机制是如何工作的,我认为:)我认为我已经接近了,但是我的IDE仍然给了我仅在promise.future行上方的单个右花括号处出现一个检查错误。

The error reads: Expression of type Unit doesn't conform to expected type Nothing . 该错误显示为: 类型为Unit的表达式不符合预期的Nothing类型 I've checked the expected return values for the Promise and onComplete code blocks, but I don't believe they expect Nothing as a return type. 我已经检查了Promise和onComplete代码块的期望返回值,但是我不相信他们期望Nothing作为返回类型。

Could somebody please explain to me what I'm missing, and also, I'm sure this can be done better, so let me know if you have any tips I can learn from! 有人可以向我解释我所缺少的,而且,我敢肯定,这样做可以做得更好,所以请告诉我您是否有任何可以借鉴的技巧!

You're kinda on the right track but as @cchantep said, once you're operating in Future -land, it would be very unusual to need to create your own with Promise.future . 您在正确的轨道上挺好的,但是正如@cchantep所说,一旦您在Future land进行操作,需要使用Promise.future创建自己的Promise.future是非常不寻常的。

In addition, it's actually quite unusual to see onComplete being used - idiomatic Scala generally favors the "higher-level" abstraction of map ping over Futures. 另外,看到使用onComplete实际上是很不寻常的-惯用的Scala通常偏向于对Future map的“更高层次”抽象。 I'll attempt to demonstrate how I'd write your function in a Play controller: 我将尝试演示如何在Play控制器中编写您的函数:

Firstly , the "endpoint" just takes care of one thing - interfacing with the outside world - ie the JSON-parsing part. 首先 ,“端点”只需要处理一件事 -与外界交互-即JSON解析部分。 If everything converts OK, it calls a private method ( performUpdateAll ) that actually does the work: 如果一切都转换为OK,它将调用一个实际完成工作的私有方法( performUpdateAll ):

def updateAll() = Action.async(parse.json) { request =>
  Json.fromJson[List[City]](request.body) match {
    case JsSuccess(givenCities, _) =>
      performUpdateAll(givenCities)
    case JsError(errors) =>
      Future.successful(BadRequest("Could not build a city from the json provided. "))
   }
}

Next , we have the private function that performs the update of multiple cities. 接下来 ,我们具有执行多个城市更新的私有功能。 Again, trying to abide by the Single Responsibility Principle (in a functional sense - one function should do one thing), I've extracted out updateCity which knows how to update exactly one city and returns a Future[UpdateWriteResult] . 再次,尝试遵守单一职责原则 (从功能上讲-一个功能应该做一件事),我提取了updateCity ,它知道如何精确地更新一个城市并返回Future[UpdateWriteResult] A nice side-effect of this is code-reuse; 这样的好处是代码重用。 you may find you'll be able to use such a function elsewhere. 您可能会发现可以在其他地方使用此功能。

private def performUpdateAll(givenCities:List[City]):Future[Result] = {

  val updateFutures = givenCities.map { city =>
    updateCity(city)
  }

  Future.sequence(updateFutures).map { listOfResults =>
    if (listOfResults.forall(_.ok)) {
      val count = listOfResults.map(_.n).sum
      Ok(s"Updated $count cities")          
    } else {
      InternalServerError("Error updating cities")
    }
  }
}

As far as I can tell, this will work in exactly the same way as you intended yours to work. 据我所知,这将与您希望的工作方式完全相同。 But by using Future.map instead of its lower-level counterpart Future.onComplete and matching on Success and Failure you get much more succinct code where (in my opinion) it's much easier to see the intent because there's less boilerplate around it. 但是,通过使用Future.map而不是其较低级别的对等方Future.onComplete并在SuccessFailure上进行匹配,您可以获得更简洁的代码(在我看来),因为围绕着它的样板更少,因此更容易看到意图

We still check that every update worked, with this: 我们仍然使用以下方法检查每个更新是否有效:

if (listOfResults.forall(_.ok)) 

which I would argue reads pretty well - all the results have to be OK! 我认为这很不错-所有结果都必须正确!

The other little trick I did to tidy up was replace your "counting" logic which used a mutable variable, with a one-liner: 我整理的另一个小技巧是用单行代码替换使用可变变量的“计数”逻辑:

var count = 0
for {
  updateWriteResult <- s.value
} yield count += updateWriteResult.n 

Becomes: 变为:

val count = listOfResults.map(_.n).sum

ie convert the list of results to a list of integers (the n in the UpdateWriteResult ) and then use the built-in sum function available on lists to do the rest. 例如,将结果列表转换为整数列表( UpdateWriteResultn ),然后使用列表上可用的内置sum函数完成其余操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM