joesan
joesan

Reputation: 15385

Scala Strange Error with Partial Functions

I have the following piece of code that fails compilation for some strange reason!

def createNewPowerPlant = Action.async(parse.tolerantJson) { request =>
    request.body.validate[PowerPlantConfig].fold(
      errors => {
        Future.successful(
          BadRequest(Json.obj("message" -> s"invalid PowerPlantConfig $errors"))
        )
      },
      success => { // fails here!!
        dbService.newPowerPlant(toPowerPlantRow(success)).recover {
          case NonFatal(ex) =>
            Future.successful { UnprocessableEntity(
              Json.obj("message" -> s"Could not create new PowerPlant because of ${ex.getMessage}")
            ) }
        }
        Future.successful { Ok("") }
      }
    )
  }

And this is what I see as the reason:

Controller.scala:103: a type was inferred to be `Any`; this may indicate a programming error.
[error]       success => {
[error]                  ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
[error] Total time: 16 s, completed Aug 21, 2017 9:46:30 AM

Any ideas as to why this error happens? I'm sure it has got something to do with the compiler as I have similar code in my controller which compiles!

Upvotes: 0

Views: 92

Answers (2)

joesan
joesan

Reputation: 15385

With the hint from Alexander Arendar

I did the following to solve this:

def createNewPowerPlant = Action.async(parse.tolerantJson) { request =>
    request.body.validate[PowerPlantConfig].fold(
      errors => {
        Future.successful(
          BadRequest(Json.obj("message" -> s"invalid PowerPlantConfig $errors"))
        )
      },
      success => {
        toPowerPlantRow(success) match {
          case None => Future.successful(
            BadRequest(Json.obj("message" -> s"invalid PowerPlantConfig ")) // TODO: fix errors
          )
          case Some(row) =>
            dbService.newPowerPlant(row).materialize.map {
              case Success(insertedRecordId) =>
                Ok("TODO: Send a Success JSON back with the id of the newly inserted record")
              case Failure(ex) =>
                UnprocessableEntity(
                  Json.obj("message" -> s"Could not create new PowerPlant because of ${ex.getMessage}")
                )
            }
        }
      }
    )
  }

Notice I'm using the materialize FutureExtensions method from the Monix library!

Upvotes: 0

Alexander Arendar
Alexander Arendar

Reputation: 3425

You have "-Xfatal-warnings" in the build.sbt. That makes the compiler to throw a compilation error where in the regular case it would be just a warning. To check this I've commented the "-Xfatal-warnings". Then sbt clean compile gives this:

alex@POSITRON /ssd2/projects/PowerPlantProblem/plant-simulator $ sbt clean compile
[info] Loading project definition from /ssd2/projects/PowerPlantProblem/plant-simulator/project
[info] Updating {file:/ssd2/projects/PowerPlantProblem/plant-simulator/project/}plant-simulator-build...
Waiting for lock on /home/alex/.ivy2/.sbt.ivy.lock to be available...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Set current project to plant-simulator (in build file:/ssd2/projects/PowerPlantProblem/plant-simulator/)
[success] Total time: 0 s, completed Aug 21, 2017 3:39:57 PM
[info] Updating {file:/ssd2/projects/PowerPlantProblem/plant-simulator/}root...
[info] Resolving jline#jline;2.14.3 ...
[info] Done updating.
[info] Compiling 31 Scala sources and 2 Java sources to /ssd2/projects/PowerPlantProblem/plant-simulator/target/scala-2.11/classes...
[warn] /ssd2/projects/PowerPlantProblem/plant-simulator/app/com/inland24/plantsim/controllers/PowerPlantController.scala:102: a type was inferred to be `Any`; this may indicate a programming error.
[warn]           case Some(row) => dbService.newPowerPlant(row) recoverWith{
[warn]                ^
[warn] one warning found
[success] Total time: 23 s, completed Aug 21, 2017 3:40:20 PM
alex@POSITRON /ssd2/projects/PowerPlantProblem/plant-simulator $ 

This means that my suggestion is true. So you either disable that "-Xfatal-warnings" or make the code to satisfy more strict requirements it brings on.

Now in order to understand that warning itself, take a look at the signature of recoverWith method:

def recoverWith[U >: T](pf: PartialFunction[Throwable, Future[U]])(implicit executor: ExecutionContext): Future[U]

In your case T is Int. So any common ancestor of Int and Future[Result] is Any of course. You can make this compilable with "-Xfatal-warnings" just by inserting an Int value like here:

case Some(row) => dbService.newPowerPlant(row) recoverWith{
  case ex: Exception => Future.successful{
    UnprocessableEntity(
      Json.obj("message" -> s"Could not create new PowerPlant because of ${ex.getMessage}")
    )
    5
  }
}

The warning makes a sense here because the code is not clean: you are trying to recover a problem with inserting a row to DB (e.g. DB server is down) by returning UnprocessableEntity HTTP status which really does not make any sense, taking into account that you override this intention after that by Future.successful { Ok("") }.

Hope this helps.

Upvotes: 1

Related Questions