Композиция Either
map и flatMap
«Композиция Either» — бесплатный урок Scala for Backend Engineering & Functional Programming на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Scala for Backend Engineering & Functional Programming, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.
Композиция вычислений
В реальных программах несколько этапов могут завершаться ошибкой: разбор, проверка, поиск, вычисление. Either позволяет объединить их так, чтобы первая ошибка немедленно прерывала всю цепочку.
Для этого используются map, flatMap и конструкции for.
flatMap выстраивает последовательность Either
flatMap применяет функцию, которая сама возвращает Either. Если получатель содержит Left, функция не вызывается, а Left передаётся дальше.
object Main {
def positive(n: Int): Either[String, Int] =
if (n > 0) Right(n) else Left("not positive")
def main(args: Array[String]): Unit = {
val r = Right(5).flatMap(positive)
val l = Right(-1).flatMap(positive)
println(r)
println(l)
}
}Прерывание цепочки
При объединении нескольких вызовов flatMap первый Left останавливает всё выполнение. Последующие этапы пропускаются, и эта ошибка становится итоговым результатом.
object Main {
def step(label: String, n: Int): Either[String, Int] = {
println(s"running $label")
Right(n + 1)
}
def main(args: Array[String]): Unit = {
val result = Left("early error").asInstanceOf[Either[String, Int]]
.flatMap(n => step("A", n))
.flatMap(n => step("B", n))
println(result)
}
}Конструкция for для Either
Поскольку Either ориентирован вправо, его можно использовать в конструкции for. Каждый оператор <- извлекает значение из Right; любой Left останавливает конструкцию и становится её результатом.
object Main {
def parse(s: String): Either[String, Int] =
s.toIntOption.toRight(s"bad: $s")
def main(args: Array[String]): Unit = {
val sum = for {
a <- parse("3")
b <- parse("4")
} yield a + b
println(sum)
}
}Конструкция for с ошибкой
Если любой этап конструкции выдаёт Left, всё выражение становится этим Left, а последующие этапы не выполняются.
object Main {
def parse(s: String): Either[String, Int] =
s.toIntOption.toRight(s"bad: $s")
def main(args: Array[String]): Unit = {
val sum = for {
a <- parse("3")
b <- parse("oops")
c <- parse("5")
} yield a + b + c
println(sum)
}
}Цепочки проверок
Реалистичная цепочка обработки: разобрать строку, затем проверить бизнес-правило и преобразовать результат. Каждый этап возвращает Either.
object Main {
def parse(s: String): Either[String, Int] = s.toIntOption.toRight("not a number")
def checkRange(n: Int): Either[String, Int] =
if (n >= 1 && n <= 100) Right(n) else Left("out of range")
def process(s: String): Either[String, Int] =
parse(s).flatMap(checkRange).map(_ * 10)
def main(args: Array[String]): Unit = {
println(process("7"))
println(process("500"))
println(process("x"))
}
}map и flatMap
Используйте map, когда функция возвращает обычное значение. Используйте flatMap, когда она возвращает другой Either, чтобы избежать вложенного Either[String, Either[String, Int]].
@main def run(): Unit = {
val withMap: Either[String, Int] = Right(2).map(_ + 1)
val nested: Either[String, Either[String, Int]] = Right(2).map(n => Right(n + 1))
val flat: Either[String, Int] = Right(2).flatMap(n => Right(n + 1))
println(withMap)
println(nested)
println(flat)
}Объединение независимых значений
Конструкция for также работает, когда каждый этап не зависит от предыдущего. Все этапы должны завершиться успешно, чтобы выполнялось итоговое выражение yield.
object Main {
def parse(s: String): Either[String, Int] = s.toIntOption.toRight(s"bad: $s")
def main(args: Array[String]): Unit = {
val combined = for {
x <- parse("10")
y <- parse("20")
z <- parse("30")
} yield List(x, y, z).sum
println(combined)
}
}Преобразование ошибок в стиле leftMap
В стандартной библиотеке Either нет leftMap, но ошибку можно преобразовать с помощью swap.map(...).swap или сопоставления внутри fold. Это помогает сохранять согласованные типы ошибок во всей цепочке обработки.
@main def run(): Unit = {
val e: Either[String, Int] = Left("low-level error")
val mapped = e.swap.map(msg => s"context: $msg").swap
println(mapped)
}Собираем всё вместе
Небольшая цепочка для калькулятора: разобрать два числа и разделить их, сообщив о любой ошибке с помощью типизированного результата.
object Main {
def parse(s: String): Either[String, Int] = s.toIntOption.toRight(s"bad number: $s")
def divide(a: Int, b: Int): Either[String, Int] =
if (b == 0) Left("division by zero") else Right(a / b)
def calc(x: String, y: String): Either[String, Int] =
for {
a <- parse(x)
b <- parse(y)
r <- divide(a, b)
} yield r
def main(args: Array[String]): Unit = {
println(calc("20", "4"))
println(calc("20", "0"))
println(calc("x", "4"))
}
}Почему это важно
Композиция Either даёт Вам программирование, ориентированное на железнодорожные пути: успешный сценарий проходит через Right, а любая ошибка сворачивает на путь Left и обходит остальные этапы. Никаких исключений, проверок на null — только значения.
Быстрая проверка
Проверьте, насколько хорошо Вы понимаете композицию Either.
Итоги
Вы научились создавать композиции с Either:
flatMapвыстраивает последовательность этапов, способных завершиться ошибкой, и прерывает её на первомLeft.- Конструкции
forделают многоэтапные цепочки обработки удобными для чтения. - Используйте
mapдля обычных результатов, аflatMap— для функций, возвращающих Either. - Преобразуйте ошибки с помощью
swap.map(...).swap.
Часто задаваемые вопросы
Урок «Композиция Either» бесплатный?
Да — полный текст урока «Композиция Either» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Scala for Backend Engineering & Functional Programming, подпишись на CoddyKit PRO. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.
Чему я научусь в уроке «Композиция Either»?
map и flatMap Ты практикуешь Scala for Backend Engineering & Functional Programming с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Scala for Backend Engineering & Functional Programming?
Предыдущий опыт не требуется. Scala for Backend Engineering & Functional Programming на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Композиция Either»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Scala for Backend Engineering & Functional Programming?
Да. Каждый урок Scala for Backend Engineering & Functional Programming включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Either для ошибок
- Try, Success, Failure
- Композиция Either
- Преобразование между типами