0

I am trying to do several dependent Slick/DB calls and then display the resulting data within a twirl template.

def show(slug: String) = Action.async { implicit rs =>

    for {

      f <- fooDAO.findBySlug(slug)  // f is of type Option[foo]
      fid <- f.flatMap(a => a.id.map(b => b)) // fid is of type Long
      b <- barDAO.findByFooId(fid) // b is of type Seq[bar] 

    } yield {

        f.map {
          case Some(f) => Ok(views.html.foobar(f, b))
          case _ => NotFound
        }

      }
  }

I first need to get the "ID" to then be able to query other relevant data. The compiler is now producing this error:

play.sbt.PlayExceptions$CompilationException: Compilation error[type mismatch;
 found   : scala.concurrent.Future[Option[play.api.mvc.Result]]
 required: Option[?]]

Any help would be greatly appreciated.

krausfm
  • 1
  • 1
  • There are several things I don't follow in your code. 1. why `f.flatMap(a => a.id.map(b => b))` instead of `f.flatMap(_.id)`; 2. why the comments use variable names different from referenced code; 3. what do you expect from the last case mapping in the `yield` block: are you aware that the `NotFound` alternative is never reached? `case f =>` will match every value and bind it to a variable named `f`. – pagoda_5b Jun 02 '15 at 21:04
  • Sorry for the mixup with the variable names. I corrected this now. – krausfm Jun 02 '15 at 21:08

2 Answers2

1

There is a fundamental flaw in your code, in that you're mixing in the same comprehension an Option and a Seq

A for-comprehension is expected to work on the same "container" type, which will be the resulting representation of the yield

e.g. if you combine several Options you get an Option, if you combine Seqs you get a Seq.

In this case you can overcome the problem by converting the Option (foo) to a Seq (which will be empty if the foo is None and have 1 element if not).

The end result would be

val results: Seq[(Foo, Bar)] =
  for {
    f <- fooDAO.findBySlug(slug).toSeq  // f is of type Seq[Foo]
    b <- barDAO.findByFooId(f.id) // b is of type Seq[Bar] 
  } yield (f, b)

But I guess this is not what you need. I suppose you want to get all Bars associated with the retrieved Foo, if any, and present it with your template. If no Foo is present for the slug, you want a NotFound.

We can do it like this

def show(slug: String) = Action.async { implicit rs =>

  val f = fooDAO.findBySlug(slug)  // f is of type Option[Foo]

  f.fold(
    NotFound,
    foo => Ok(views.html.foobar(foo, barDAO.findByFooId(foo.id))
  )

}

You can make it more explicit by defining a supporting method

def show(slug: String) = Action.async { implicit rs =>

  def barsOf(f: Foo): Seq[Bar] = barDAO.findByFooId(f.id)

  val f = fooDAO.findBySlug(slug)  // f is of type Option[Foo]

  f.fold(
    NotFound,
    foo => Ok(views.html.foobar(foo, barsOf(foo))
  )

}
pagoda_5b
  • 7,333
  • 1
  • 27
  • 40
0

It's a bit tricky understanding what you're trying to achieve here, but if the whole thing is predicated on the findbySlug returning a Future[Option[Foo]] and the eventual outcome being a NotFound if that Option is a None, then your yield should probably just be:

   ...
 } yield {
  f.fold(NotFound)(foo => Ok(views.html.foobar(foo, b)))
}

Option[T] is a fantastic type for data-retrieval and control-flow, but pattern-matching on it is almost never the right approach. The use of fold feels nicely succinct for the task.

millhouse
  • 9,817
  • 4
  • 32
  • 40