0Pricing
Scala for Backend Engineering & Functional Programming · Урок

Использование Option и Future

Монадическое объединение в цепочку

«Использование Option и Future» — бесплатный урок Scala for Backend Engineering & Functional Programming на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения 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 for-выражение создаёт результат только в том случае, если каждый генератор содержит 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)
  }
}

Цепочка необязательных поисковых операций

Распространённый шаблон: поиск ключей в Map, где каждая операция поиска возвращает Option. 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

Защитное условие внутри for-выражения с 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-выражения, Future выполняются последовательно, поскольку каждый flatMap ожидает результат предыдущего.

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 прерывает вычисление и возвращается как результат.

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)
  }
}

Одна форма для разных типов

Обратите внимание: for-выражение выглядит одинаково при использовании 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)
  }
}

Смешивать типы нельзя

Все генераторы одного for-выражения должны принадлежать одной и той же монаде. Нельзя напрямую смешивать генератор 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)
  }
}

Быстрая проверка

Что происходит в for-выражении с Option, если один из генераторов выдаёт None?

Итоги

Вы узнали о монадной цепочке с for-выражениями:

  • Она работает с любым типом, содержащим map и flatMap
  • Option прерывает вычисление при None
  • Either и Try прерывают вычисление при ошибке, сохраняя сведения об ошибке
  • Future выстраивает асинхронные шаги в последовательность; для параллельного выполнения запускайте их заранее
  • Все генераторы должны использовать один и тот же тип монады

Часто задаваемые вопросы

Урок «Использование Option и Future» бесплатный?

Да — полный текст урока «Использование Option и Future» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Scala for Backend Engineering & Functional Programming, подпишись на CoddyKit PRO. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.

Чему я научусь в уроке «Использование Option и Future»?

Монадическое объединение в цепочку Ты практикуешь Scala for Backend Engineering & Functional Programming с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Scala for Backend Engineering & Functional Programming?

Предыдущий опыт не требуется. Scala for Backend Engineering & Functional Programming на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Использование Option и Future»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Scala for Backend Engineering & Functional Programming?

Да. Каждый урок Scala for Backend Engineering & Functional Programming включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Десахаризация for
  2. Фильтрация с помощью if
  3. Несколько генераторов
  4. Использование Option и Future
← Назад к Scala for Backend Engineering & Functional Programming