LoginSignup
1
0

More than 5 years have passed since last update.

Future Option 地獄の対処法 - OptionTの使い方 (Scala)

Last updated at Posted at 2019-03-16

実コードでは引数の依存や仕様で順番は変えられないものを想定
OptionTを使わない場合と使う場合のコード例
for式を二重にする方法は取り出した値がOptionに包まれた状態で、次のgeneratorで利用できないためここでは触れない

import scalaz.OptionT
import scala.concurrent.{Await, ExecutionContext, Future}
import scala.concurrent.duration.Duration

object Hello extends App {
  import scala.concurrent.ExecutionContext.Implicits.global
  import scalaz.Scalaz._
  import Factories._

  // 実コードでは引数の依存や仕様で順番は変えられないものを想定
  private val option: Option[String]                = Option("hoge")
  private val futureOption: Future[Option[String]]  = Future.successful(Some("fuga"))
  private val future: Future[Piyo]                  = Future.successful(Piyo("piyo", "piyo"))
  private val futureOption2: Future[Option[String]] = Future.successful(Some("moge"))

// OptionTを使わない場合
  val futureOptionString: Future[Option[String]] = option
    .map(
      hoge =>
        futureOption.flatMap(
          _.map(
            fuga =>
              future.flatMap {
                case Piyo(piyo, piyopiyo) =>
                  futureOption2.map(_.map(moge => s"$hoge $fuga $piyo $piyopiyo $moge"))
            }
          ).getOrElse(Future.successful(None))
      )
    ).getOrElse(Future.successful(None))
  val res1: Option[String] = Await.result(futureOptionString, Duration.Inf)

// OptionTを使った場合
  val optionT: OptionT[Future, String] = for {
    hoge                 <- option.toOptionT
    fuga                 <- futureOption.toOptionT
    Piyo(piyo, piyopiyo) <- future.toOptionT
    moge                 <- futureOption2.toOptionT
  } yield {
    s"$hoge $fuga $piyo $piyopiyo $moge"
  }
  val res2: Option[String] = Await.result(optionT.run, Duration.Inf)

  println(res1 == res2)
}

object Factories {
  import scalaz._
  implicit class RichOption[A](val opt: Option[A]) {
    def toOptionT(implicit ec: ExecutionContext): OptionT[Future, A] = OptionT(Future.successful(opt))
  }
  implicit class RichFutureOption[A](val f: Future[Option[A]]) {
    def toOptionT(implicit ec: ExecutionContext): OptionT[Future, A]               = OptionT(f)
    def toOptionTOption(implicit ec: ExecutionContext): OptionT[Future, Option[A]] = OptionT(f.map(Option(_)))
  }
  implicit class RichFuture[A](val f: Future[A]) {
    def toOptionT(implicit ec: ExecutionContext): OptionT[Future, A] = OptionT(f.map(Option(_)))
  }
}
case class Piyo(piyo: String, piyopiyo: String)

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0