3

I'm in stack to understand how Scala for works. I think codes below could be written with for though I don't have any idea how. Could someone explain me how I can do that?

def foo: Future[Option[Int]] = ???
def bar: Future[Throwable Xor Option[Int]] = ???
def baz: Future[Option[Boolean]] = ???

foo.flatMap {
  case Some(x) =>
    Future.successful(x)
  case None =>
    bar.flatMap {
      case Xor.Right(Some(x)) =>
        baz.map {
          case true => 1
          case false => 0
        }
      case Xor.Right(None) =>
        Future.successful(0)
      case Xor.Left(_) =>
        Future.successful(-1)
    }
}
suish
  • 3,253
  • 1
  • 15
  • 34
  • Maybe this will help : http://stackoverflow.com/questions/19045936/scalas-for-comprehension-with-futures – meucaa May 26 '16 at 10:07

2 Answers2

4

With all the branching inside the flatMap functions, it won't be possible to write this as a for comprehension.

It is possible to replace pattern matching with a fold method, but this may be a matter of personal preference.

Option("suish") match {
  case Some(name) => s"hello $name"
  case None => "hello world"
}
// is analogous to
Option("suish").fold("hello world")(name => s"hello $name")

I rewrote your pattern matching using the fold methods of OptionT (tutorial), XorT and Option, but I'm not sure if this is more readable than your nested pattern matches.

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import cats.implicits._
import cats.data.{Xor, XorT, OptionT}

def foo: Future[Option[Int]] = none[Int].pure[Future]
def bar: Future[Throwable Xor Option[Int]] = 2.some.right.pure[Future]
def baz: Future[Option[Boolean]] = true.some.pure[Future]

OptionT(foo).getOrElseF {
  // foo : case None
  XorT(bar).fold(
    // bar : case Xor.Left
    _ => -1.pure[Future],
    barO => barO.fold(
      // bar : case Xor.Right(None)
      0.pure[Future])(
      // bar : case Xor.Right(Some(x))
      _ => baz.map(_.fold(0 /* ? */)(b => if (b) 1 else 0)))
  ).flatten
}
// Future[Int]
Peter Neyens
  • 9,770
  • 27
  • 33
3

It is not possible to pattern match in for-comprehensions. See Allow pattern matching on type in for comprehensions.

Perhaps you can use Monad Transformers. I do want to implement your code in that manner, but I don't have time for that now. Maybe the hint can help you.

/Edit This is not completely correct as pointed out by the comment of Sergey. You can pattern match in for-comprehensions as long as all matches are of the same type. See this image taken from the second lesson of the first week of Functional Program Design in Scala Coursera Course where all patterns inherit from JSON:

For-comprehension and pattern matching

user2609980
  • 10,264
  • 15
  • 74
  • 143
  • 2
    This is correct, with one addition: It actually _is_ possible to pattern match in a for-comprehension, however, you are able to provide only one case for the match, and it acts as a filter instead of throwing MatchError: `for { Some(x) <- List(Some(1), None, Some(2)) } yield x` would yield a `List(1, 2)` – Sergey May 26 '16 at 12:35
  • 1
    That first presentation you linked is the most useful resource on understanding advanced Scala I've ever read. – LMeyer May 30 '16 at 15:04