1
votes

I'm new to Scala and I'm building a Play app to learn Scala and Slick. The app has todo lists and each list has items. Each user has multiple lists. I've written a controller to get the list as JSON, and the JSON includes all the items in the list.

  def viewList(id: Long) = AuthenticatedAction.async { implicit request =>
    val userId = request.user.toLong;
    db.run(todoLists.filter(listById(id, userId)).result.headOption).flatMap { list =>
      list match {
        case Some(_) => {
          val json = toJson(list).as[JsObject]
          // Fetch the items in this list and add them to the JSON response
          db.run(todoItems.filter(_.listId === id).sortBy(_.text).result).map { items => 
            Ok(json + ("items" -> toJson(items)))
          }
        }
        case None => Future.successful(NotFound)
      }
    }
  }

Is it possible to write this function using a for comprehension? I have a nested flatMap+map call, so it seems like it should be possible.

1
every call to map, flatMap, filter can be rewritten as for comprehension, but do you really need it? This can be more verbose - ka4eli
That's an excellent question - I'm new to Scala so I don't really know. I've been told that for comprehension can make things easier to read. - Nathan
if you understand the code with higher order functions, I think you don't. May be just for practising or for someone else better understanding, imho - ka4eli

1 Answers

3
votes

Yes, it is possible. Like this:

def viewList(id: Long) = AuthenticatedAction.async { implicit request =>
  val userId = request.user.toLong
  for {
    list <- db.run(todoLists.filter(listById(id, userId)).result.headOption)
    resp <- list.fold[Future[Result]](Future.successful(NotFound)) { _ =>
      val json = toJson(list).as[JsObject]
      // Fetch the items in this list and add them to the JSON response
      db.run(todoItems.filter(_.listId === id).sortBy(_.text).result).map { items => 
        Ok(json + ("items" -> toJson(items)))
      }
    }
  } yield resp 
}

Another variant:

  def viewList(id: Long) = AuthenticatedAction.async { implicit request =>
    val userId = request.user.toLong
    for {
      list <- db.run(todoLists.filter(listById(id, userId)).result.headOption)
      items <- db.run(todoItems.filter(_.listId === id).sortBy(_.text).result)
    } yield list match {
      case Some(_) => Ok(toJson(list).as[JsObject] + ("items" -> toJson(items)))
      case None => NotFound
    }
  }