Option と Future の利用
モナドを連鎖させます。
「Option と Future の利用」はCoddyKit上の無料Scala for Backend Engineering & Functional Programmingレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはScala for Backend Engineering & Functional Programming学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。
コレクションを超えて
for内包表記はリストに限定されません。mapとflatMapを提供する型であれば使用でき、Option、Either、Try、Futureなどが該当します。これはモナド的な連鎖と呼ばれます。
object Main {
def main(args: Array[String]): Unit = {
val sum = for {
a <- Some(10)
b <- Some(20)
} yield a + b
println(sum)
}
}OptionはNoneで短絡する
Optionでは、すべてのジェネレーターがSomeの場合にのみ、内包表記から結果が生成されます。いずれかがNoneになると、全体の結果がNoneになり、後続のステップはスキップされます。
object Main {
def main(args: Array[String]): Unit = {
val ok = for {
a <- Some(2)
b <- Some(3)
} yield a * b
val missing = for {
a <- Some(2)
b <- None: Option[Int]
} yield a * b
println(ok)
println(missing)
}
}オプショナルな検索を連鎖させる
よくあるパターンは、各検索がOptionを返すMapでキーを検索することです。for内包表記からは、すべての検索が成功した場合にのみ値が生成されます。
object Main {
def main(args: Array[String]): Unit = {
val prices = Map("apple" -> 3, "bread" -> 5)
val total = for {
a <- prices.get("apple")
b <- prices.get("bread")
} yield a + b
println(total)
val missing = for {
a <- prices.get("apple")
c <- prices.get("milk")
} yield a + c
println(missing)
}
}ネストしたif-elseより優れている理由
for内包表記を使わない場合は、パターンマッチやnullチェックをネストする必要があります。モナド形式なら、そのピラミッド状の構造をすっきりした直線的な流れにできます。
object Main {
def parse(s: String): Option[Int] = s.toIntOption
def main(args: Array[String]): Unit = {
val result = for {
x <- parse("40")
y <- parse("2")
} yield x + y
println(result)
}
}ガードはOptionでも使える
Optionの内包表記内にガードを置くと、条件を満たさない場合に値がNoneになり、インラインの検証として機能します。
object Main {
def main(args: Array[String]): Unit = {
def validate(age: Int): Option[Int] =
for {
a <- Some(age)
if a >= 18
} yield a
println(validate(25))
println(validate(15))
}
}Futureの概要
Futureは、後で利用可能になる値を表します。Optionと同様にmapとflatMapを持つため、for内包表記で非同期処理のステップを順番に実行できます。
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration._
object Main {
def main(args: Array[String]): Unit = {
val f = for {
a <- Future(10)
b <- Future(20)
} yield a + b
println(Await.result(f, 2.seconds))
}
}ここではFutureが順番に実行される
for内包表記内で一方のジェネレーターが別のジェネレーターに依存している場合、各flatMapが前の結果を待つため、Futureは順番に実行されます。
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration._
object Main {
def main(args: Array[String]): Unit = {
val f = for {
a <- Future(5)
b <- Future(a * 2)
} yield a + b
println(Await.result(f, 2.seconds))
}
}Futureを並列実行する
Futureを並列に実行するには、for内包表記の前に開始してから組み合わせます。独立した処理が待ち合わせずに重なって実行されます。
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration._
object Main {
def main(args: Array[String]): Unit = {
val fa = Future(100)
val fb = Future(200)
val combined = for {
a <- fa
b <- fb
} yield a + b
println(Await.result(combined, 2.seconds))
}
}より詳細なエラーを扱うEither
Either[E, A]は、失敗時にエラー値を保持します。for内包表記では、最初のLeftで短絡し、そのLeftが返されます。
object Main {
def parse(s: String): Either[String, Int] =
s.toIntOption.toRight(s"not a number: $s")
def main(args: Array[String]): Unit = {
val good = for { a <- parse("3"); b <- parse("4") } yield a + b
val bad = for { a <- parse("3"); b <- parse("x") } yield a + b
println(good)
println(bad)
}
}同じ形を多くの型で使える
Option、Either、Try、Futureのどれを使っても、内包表記の見た目は同じであることに注目してください。異なるのは失敗時のセマンティクスだけです。この一貫性こそが、モナド的な連鎖の力です。
import scala.util.Try
object Main {
def main(args: Array[String]): Unit = {
val result = for {
a <- Try("21".toInt)
b <- Try("2".toInt)
} yield a * b
println(result)
}
}型を混在させることはできない
1つの内包表記に含まれるすべてのジェネレーターは、同じモナドでなければなりません。OptionのジェネレーターとFutureのジェネレーターを直接混在させることはできません。先にどちらかを変換してください(たとえばFuture.fromTryを使うか、Optionをラップします)。
object Main {
def main(args: Array[String]): Unit = {
// Convert the Option to keep types consistent
val maybe: Option[Int] = Some(7)
val result = for {
a <- maybe
b <- maybe.map(_ + 1)
} yield a + b
println(result)
}
}クイックチェック
Optionのfor内包表記で、いずれかのジェネレーターがNoneを生成するとどうなりますか?
まとめ
for内包表記によるモナド的な連鎖について学びました。
mapとflatMapを持つ任意の型で動作しますOptionはNoneで短絡しますEitherとTryは失敗時に短絡し、エラー情報を保持しますFutureは非同期処理のステップを順番に実行します。並列化するには早めに開始します- すべてのジェネレーターが同じモナド型を共有する必要があります
AI チューターと学ぶ Scala — 無料
ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。
- コース
- 39
- レッスン
- 143
よくある質問
「Option と Future の利用」レッスンは無料ですか?
はい。「Option と Future の利用」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Scala for Backend Engineering & Functional Programmingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Scala for Backend Engineering & Functional Programmingコースには全4レッスンが含まれています。
「Option と Future の利用」で何を学びますか?
モナドを連鎖させます。 ブラウザで直接実行するハンズオンコードでScala for Backend Engineering & Functional Programmingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Scala for Backend Engineering & Functional Programmingを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのScala for Backend Engineering & Functional Programmingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「Option と Future の利用」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このScala for Backend Engineering & Functional Programmingレッスンでコードを書いて実行できますか?
はい。すべてのScala for Backend Engineering & Functional Programmingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- for の脱糖
- if によるフィルタリング
- 複数のジェネレーター
- Option と Future の利用