Шаблон классов типов
Аd hoc-полиморфизм
«Шаблон классов типов» — бесплатный урок Scala for Backend Engineering & Functional Programming на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Scala for Backend Engineering & Functional Programming, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.
Что такое класс типов?
Класс типов — это способ добавлять поведение к типам, не изменяя их. Это форма непараметрического полиморфизма: одна и та же операция работает со множеством не связанных между собой типов, а компилятор выбирает реализацию по типу.
Этот подход появился в Haskell и широко используется в библиотеках Scala, например Cats.
Какую проблему он решает
Наследование встраивает поведение в тип во время его определения. Но часто Вы не можете изменить тип, потому что он находится в библиотеке, или хотите разное поведение в разных контекстах. Классы типов позволяют присоединять поведение внешним образом.
Шаг 1: определение трейта
Класс типов — это параметризованный типом trait. Здесь Show[A] описывает способ отображения любого A в String.
trait Show[A] {
def show(value: A): String
}
@main def run(): Unit = {
println("Show trait defined")
}Шаг 2: предоставление экземпляров
Для каждого конкретного типа, который Вы хотите поддерживать, создайте instance трейта. Это и есть экземпляры класса типов.
trait Show[A] { def show(value: A): String }
object Main {
val intShow: Show[Int] = (v: Int) => s"Int($v)"
val strShow: Show[String] = (v: String) => s"Str($v)"
def main(args: Array[String]): Unit = {
println(intShow.show(7))
println(strShow.show("hi"))
}
}Шаг 3: использование экземпляра
Функция принимает экземпляр в качестве параметра. Одна и та же функция render работает с любым типом, для которого есть экземпляр Show.
trait Show[A] { def show(value: A): String }
object Main {
def render[A](value: A, s: Show[A]): String = s.show(value)
val intShow: Show[Int] = (v: Int) => s"<$v>"
def main(args: Array[String]): Unit = {
println(render(99, intShow))
}
}Неявная передача
Передавать экземпляры вручную утомительно. Если пометить экземпляр и параметр как implicit (или использовать using в Scala 3), компилятор сможет подставлять экземпляр автоматически.
trait Show[A] { def show(value: A): String }
object Main {
implicit val intShow: Show[Int] = (v: Int) => s"<$v>"
def render[A](value: A)(implicit s: Show[A]): String = s.show(value)
def main(args: Array[String]): Unit = {
println(render(42))
}
}Непараметрический полиморфизм
Одно и то же имя функции направляет вызов к разным реализациям в зависимости от типа аргумента. Это непараметрический полиморфизм, отличный от полиморфизма подтипов (наследования) и параметрического полиморфизма (обобщений).
trait Show[A] { def show(value: A): String }
object Main {
implicit val intShow: Show[Int] = (v: Int) => s"int:$v"
implicit val boolShow: Show[Boolean] = (v: Boolean) => s"bool:$v"
def render[A](value: A)(implicit s: Show[A]): String = s.show(value)
def main(args: Array[String]): Unit = {
println(render(5))
println(render(true))
}
}Работа с собственными типами
В этом и заключается настоящая сила подхода: Вы можете аккуратно добавлять поведение к собственным типам, не загружая класс данных задачами форматирования.
trait Show[A] { def show(value: A): String }
case class User(name: String, age: Int)
object Main {
implicit val userShow: Show[User] = (u: User) => s"${u.name} (${u.age})"
def render[A](value: A)(implicit s: Show[A]): String = s.show(value)
def main(args: Array[String]): Unit = {
println(render(User("Ada", 36)))
}
}Классы типов и интерфейсы
Интерфейс связывает поведение с определением типа, а класс типов отделяет их друг от друга.
- Вы можете добавить экземпляр класса типов для типа, которым не владеете.
- Вы можете иметь несколько экземпляров для разных контекстов.
- Компилятор выбирает поведение в месте вызова.
Три компонента
У каждого класса типов есть три части:
- Трейт — абстрактная операция.
- Экземпляры — реализации для конкретных типов.
- Интерфейс — функции, которым требуется экземпляр.
В следующих уроках Вы рассмотрите каждую из них.
Полный небольшой пример
Объединим все элементы подхода в обобщённой функции, использующей неявный экземпляр.
trait Show[A] { def show(value: A): String }
object Main {
implicit val intShow: Show[Int] = (v: Int) => s"#$v"
implicit val strShow: Show[String] = (v: String) => '"' + v + '"'
def printAll[A](xs: List[A])(implicit s: Show[A]): Unit =
xs.foreach(x => println(s.show(x)))
def main(args: Array[String]): Unit = {
printAll(List(1, 2, 3))
printAll(List("a", "b"))
}
}Быстрая проверка
Проверьте, насколько хорошо Вы поняли подход с классами типов.
Итоги
Вы изучили подход с классами типов:
- Параметризованный
traitописывает операцию. - Экземпляры реализуют её для конкретных типов.
- Параметры
implicitпозволяют компилятору подставлять экземпляр. - Это обеспечивает непараметрический полиморфизм и позволяет расширять типы, которыми Вы не владеете.
Часто задаваемые вопросы
Урок «Шаблон классов типов» бесплатный?
Да — полный текст урока «Шаблон классов типов» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Scala for Backend Engineering & Functional Programming, подпишись на CoddyKit PRO. Курс Scala for Backend Engineering & Functional Programming содержит 4 уроков всего.
Чему я научусь в уроке «Шаблон классов типов»?
Аd hoc-полиморфизм Ты практикуешь Scala for Backend Engineering & Functional Programming с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Scala for Backend Engineering & Functional Programming?
Предыдущий опыт не требуется. Scala for Backend Engineering & Functional Programming на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Шаблон классов типов»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Scala for Backend Engineering & Functional Programming?
Да. Каждый урок Scala for Backend Engineering & Functional Programming включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Шаблон классов типов
- Определение экземпляров
- Распространённые классы типов
- Выведение классов типов