Scala for Backend Engineering & Functional Programming · レッスン

Scalaのモナドを理解する

モナドの概念を明らかにし、関数型の方法で計算を順番に合成できる仕組みを学びます。

レッスン 2/312 ステップ

「Scalaのモナドを理解する」はCoddyKit上の無料Scala for Backend Engineering & Functional Programmingレッスンです。 これはレッスン2/3です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはScala for Backend Engineering & Functional Programming学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Scala for Backend Engineering & Functional Programmingコースには全3レッスンが含まれています。

Monadとは

Monadは関数型プログラミングにおける基本的な概念で、しばしば高度なものと考えられています。ご安心ください。この概念をわかりやすく解説します。

Monadの本質は、「コンテキスト」を伴う計算を順番に実行するためのデザインパターンです。副作用を管理したり、値が存在しない可能性に対処したりする方法を、予測可能な構造の中で提供するものだと考えてください。

コンテキストを伴う処理の連結

Option[Int]のように、存在する場合と存在しない場合がある値を考えてみましょう。その値が存在する場合にのみ、複数の処理を実行したいとき、どのようにすっきり書けばよいでしょうか。

ネストしたif文はすぐに複雑になります。Monadを使うと、これらの処理を順番に連結でき、存在・不在などの「コンテキスト」が自動的に引き継がれます。

`flatMap`の力

Monadを理解するうえで最も重要な操作はflatMapです。

  • 別の「コンテキスト付き」の値(OptionやListなど)を返す関数を受け取ります。
  • 現在のコンテキストの内部にある値へ、その関数を適用します。
  • コンテキストが空の場合(たとえばNoneの場合)、関数を適用せず、空の状態をそのまま引き継ぎます。

これにより、途中で発生する可能性のある失敗や値の不在に対処しながら、処理を自然に連結できます。

`Option`と`flatMap`

ScalaのOption型は、Monadの完全な例です。OptionはSome(value)またはNoneになります。

OptionでflatMapを使うと、次のように動作します。

  • Some(value)の場合、指定した関数がvalueに適用され、その結果である別のOptionが使われます。
  • Noneの場合、関数は呼び出されず、Noneがそのまま返されます。

これにより、実際に値が存在する場合にのみ処理が実行されます。

`Option`での`flatMap`

OptionでflatMapがどのように動作するか見てみましょう。このコードは、文字列を整数に変換し、両方の処理が成功した場合にのみ、その値を2倍にします。

object Main {
  def parseToInt(s: String): Option[Int] =
    try {
      Some(s.toInt)
    } catch {
      case _: NumberFormatException => None
    }

  def main(args: Array[String]): Unit = {
    val result1 = parseToInt("10").flatMap(x => Some(x * 2))
    val result2 = parseToInt("hello").flatMap(x => Some(x * 2))

    println(s"Result 1: $result1")
    println(s"Result 2: $result2")
  }
}

`List`と`flatMap`

Monadとして振る舞う、Scalaのもう1つの一般的な型がListです。

ListでflatMapを使うと、次のように動作します。

  • 指定した関数をリストの各要素に適用します。
  • 関数は各要素について新しいListを返す必要があります。
  • その後、結果として得られたすべてのリストを連結し、1つの平坦化されたリストにします。

これは、データのリストを変換して組み合わせる場合に便利です。

`List`での`flatMap`

ListでflatMapがどのように動作するか見てみましょう。各項目に対してリストを返す関数を適用した結果が、どのように「平坦化」されるかに注目してください。

object Main {
  def main(args: Array[String]): Unit = {
    val numbers = List(1, 2, 3)

    // For each number, create a list of that number and its double
    val result = numbers.flatMap(n => List(n, n * 2))

    println(s"Original: $numbers")
    println(s"FlatMapped: $result")

    val words = List("hello", "world")
    val chars = words.flatMap(_.toList) // Get all characters

    println(s"Words: $words")
    println(s"Chars: $chars")
  }
}

Monadの規則(簡略版)

flatMapが中心的な操作である一方、真のMonadは予測可能な動作を保証するため、いくつかの法則(規則)にも従います。

簡単に言うと、次の条件を満たす型はモナド的です。

  • 値を「ラップ」できます(pureまたはunitと呼ばれることがよくあります)。
  • コンテキストを維持しながら計算を連結するflatMap操作を持ちます。

これらの法則により、モナド操作をどのようにグループ化しても、合成した結果が一貫したものになります。

MonadとFor内包表記

Scalaのfor-comprehensionsは、Monad(およびFunctorやApplicativeのような他の型)を扱うための構文糖です。

コンテキスト付きの値に対する逐次的な処理を、命令型コードに似た、より読みやすい形で記述できます。

内部では、Scalaコンパイラがfor-comprehensionsを一連のflatMap、map、filterの呼び出しへ変換します。

For内包表記での`Option`

この例では、先ほどのOption.flatMapの連鎖をfor-comprehensionで簡潔に書く方法を示します。Noneの場合も自動的に処理されます。

object Main {
  def parseToInt(s: String): Option[Int] =
    try {
      Some(s.toInt)
    } catch {
      case _: NumberFormatException => None
    }

  def main(args: Array[String]): Unit = {
    val numStr1 = "10"
    val numStr2 = "5"
    val badStr = "abc"

    val result1 = for {
      a <- parseToInt(numStr1) // If parseToInt returns None, the whole for-comp becomes None
      b <- parseToInt(numStr2)
    } yield a + b

    val result2 = for {
      a <- parseToInt(numStr1)
      b <- parseToInt(badStr) // This will be None
    } yield a + b

    println(s"Sum 1: $result1") // Some(15)
    println(s"Sum 2: $result2") // None
  }
}

Monadチャレンジ

次のScalaコードについて考えてみましょう。

val list1 = List(1, 2)
val list2 = List(10, 20)

val result = for {
  x <- list1
  y <- list2
} yield x * y

Monad:コンテキストを伴う逐次処理

おめでとうございます。Monadの理解に向けて大きく前進しました。

  • Monadは、「コンテキスト」(存在・不在を表すOptionや、複数の値を表すListなど)の中で動作する計算を順番に実行するための強力なパターンです。
  • 中心となる操作はflatMapです。新しいコンテキスト付きの値を返す関数を適用し、コンテキストを効果的に連結して平坦化します。
  • Scalaのfor-comprehensionsは優れた構文糖であり、flatMap(およびmap/filter)の呼び出しに直接変換されるため、モナドを使ったコードを大幅に読みやすくできます。

次は、これらのモナドの概念を幅広く利用するCatsやZIOなど、代表的な関数型プログラミングライブラリについて学びます。

無料で開始

AI チューターと学ぶ Scala — 無料

ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。

コース
39
レッスン
143

よくある質問

「Scalaのモナドを理解する」レッスンは無料ですか?

はい。「Scalaのモナドを理解する」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Scala for Backend Engineering & Functional Programmingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Scala for Backend Engineering & Functional Programmingコースには全3レッスンが含まれています。

「Scalaのモナドを理解する」で何を学びますか?

モナドの概念を明らかにし、関数型の方法で計算を順番に合成できる仕組みを学びます。 ブラウザで直接実行するハンズオンコードでScala for Backend Engineering & Functional Programmingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Scala for Backend Engineering & Functional Programmingを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのScala for Backend Engineering & Functional Programmingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/3です。

「Scalaのモナドを理解する」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このScala for Backend Engineering & Functional Programmingレッスンでコードを書いて実行できますか?

はい。すべてのScala for Backend Engineering & Functional Programmingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. ファンクターとアプリカティブの基礎
  2. Scalaのモナドを理解する
  3. CatsとZIOを探る
← Scala for Backend Engineering & Functional Programmingに戻る