Алгебраические типы данных
Моделируйте предметную область
«Алгебраические типы данных» — бесплатный урок 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 уроков всего.
Что такое ADT
Алгебраический тип данных (ADT) — это тип, созданный объединением других типов. В Scala ADT моделируют с помощью sealed trait и вариантов case class и case object.
ADT позволяют точно описывать структуру данных предметной области.
Типы-суммы
Тип-сумма означает, что значение является одним из нескольких вариантов (OR). Светофор — это красный OR жёлтый OR зелёный.
Это выражается с помощью запечатанного трейта и нескольких вариантов.
sealed trait Light
case object Red extends Light
case object Yellow extends Light
case object Green extends Light
object Main {
def main(args: Array[String]): Unit = {
val l: Light = Yellow
println(l)
}
}Типы-произведения
Тип-произведение означает, что значение объединяет несколько полей (AND). Точка имеет координату x AND координату y.
Классы вариантов являются типами-произведениями.
case class Point(x: Int, y: Int)
object Main {
def main(args: Array[String]): Unit = {
val p = Point(3, 4)
println(s"${p.x} and ${p.y}")
}
}Сочетание типов-сумм и типов-произведений
Настоящие ADT объединяют оба подхода: запечатанный трейт (сумму), варианты которого являются классами вариантов (произведениями).
Здесь Shape — это Circle (с радиусом) OR Rectangle (с шириной и высотой).
sealed trait Shape
case class Circle(radius: Double) extends Shape
case class Rectangle(w: Double, h: Double) extends Shape
object Main {
def main(args: Array[String]): Unit = {
val s: Shape = Rectangle(3, 4)
println(s)
}
}Работа с ADT
ADT обрабатывают с помощью сопоставления с образцом по его вариантам. Каждая ветвь обрабатывает одну фигуру.
sealed trait Shape
case class Circle(radius: Double) extends Shape
case class Rectangle(w: Double, h: Double) extends Shape
object Main {
def area(s: Shape): Double = s match {
case Circle(r) => 3.14159 * r * r
case Rectangle(w, h) => w * h
}
def main(args: Array[String]): Unit = {
println(area(Circle(2)))
println(area(Rectangle(3, 4)))
}
}Моделирование предметной области
ADT делают недопустимые состояния непредставимыми. Смоделируйте способ оплаты как закрытый набор допустимых вариантов.
sealed trait PaymentMethod
case class CreditCard(number: String) extends PaymentMethod
case class BankTransfer(iban: String) extends PaymentMethod
case object Cash extends PaymentMethod
object Main {
def main(args: Array[String]): Unit = {
val m: PaymentMethod = Cash
println(m)
}
}Рекурсивные ADT
ADT может ссылаться на самого себя — так моделируют деревья и списки. Вот простое двоичное дерево.
sealed trait Tree
case object Leaf extends Tree
case class Node(value: Int, left: Tree, right: Tree) extends Tree
object Main {
def main(args: Array[String]): Unit = {
val t: Tree = Node(1, Leaf, Node(2, Leaf, Leaf))
println(t)
}
}Свёртка рекурсивного ADT
Рекурсия вместе с сопоставлением позволяет обработать всю структуру. Здесь мы суммируем каждое значение в дереве.
sealed trait Tree
case object Leaf extends Tree
case class Node(value: Int, left: Tree, right: Tree) extends Tree
object Main {
def sum(t: Tree): Int = t match {
case Leaf => 0
case Node(v, l, r) => v + sum(l) + sum(r)
}
def main(args: Array[String]): Unit = {
val t = Node(1, Node(2, Leaf, Leaf), Node(3, Leaf, Leaf))
println(sum(t))
}
}Зачем нужны ADT
ADT мощны, потому что они:
- Точно описывают данные как суммы и произведения
- Делают невозможным создание недопустимых состояний
- Идеально сочетаются с полным сопоставлением с образцом
- Масштабируются до рекурсивных структур, таких как деревья и списки
ADT и наследование
Традиционный ООП-код распределял бы поведение по подклассам. ADT оставляют определения данных закрытыми, а поведение помещают в функции, выполняющие сопоставление с образцом.
Такое разделение данных и поведения — отличительная черта функционального проектирования.
Объединяем всё вместе
Полный небольшой ADT с функцией, которая обрабатывает каждый вариант.
sealed trait Json
case class JNum(n: Double) extends Json
case class JStr(s: String) extends Json
case object JNull extends Json
object Main {
def render(j: Json): String = j match {
case JNum(n) => n.toString
case JStr(s) => "\"" + s + "\""
case JNull => "null"
}
def main(args: Array[String]): Unit = {
List(JNum(3.0), JStr("hi"), JNull).foreach(j => println(render(j)))
}
}Быстрая проверка
Проверьте, насколько хорошо вы поняли ADT.
Итоги
Вы научились создавать алгебраические типы данных:
- Типы-суммы: запечатанный трейт с несколькими вариантами (OR)
- Типы-произведения: классы вариантов с полями (AND)
- Объединяйте их для точного моделирования предметных областей
- ADT могут быть рекурсивными (деревья, списки)
- Обрабатывайте их с помощью сопоставления с образцом
Часто задаваемые вопросы
Урок «Алгебраические типы данных» бесплатный?
Да — полный текст урока «Алгебраические типы данных» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Scala for Backend Engineering & Functional Programming, подпишись на CoddyKit PRO. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.
Чему я научусь в уроке «Алгебраические типы данных»?
Моделируйте предметную область Ты практикуешь Scala for Backend Engineering & Functional Programming с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Scala for Backend Engineering & Functional Programming?
Предыдущий опыт не требуется. Scala for Backend Engineering & Functional Programming на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Алгебраические типы данных»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Scala for Backend Engineering & Functional Programming?
Да. Каждый урок Scala for Backend Engineering & Functional Programming включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Классы case
- Запечатанные трейты
- Алгебраические типы данных
- Полное сопоставление