Scala предлагает две важные функции для контекстной абстракции:
- Параметры контекста позволяют указать параметры, которые на стороне вызова могут быть опущены программистом и должны автоматически предоставляться контекстом.
- Экземпляры given (в Scala 3) или неявные определения (в Scala 2) — это термины, которые компилятор Scala может использовать для заполнения отсутствующих аргументов.
Параметры контекста
При проектировании системы зачастую необходимо предоставлять контекстную информацию, такую как конфигурация или настройки, различным компонентам вашей системы. Одним из распространенных способов добиться этого является передача конфигурации в качестве дополнительного аргумента методам.
В следующем примере мы определяем кейс класс Config
для моделирования некоторой конфигурации веб-сайта
и передаем ее в различных методах.
case class Config(port: Int, baseUrl: String)
def renderWebsite(path: String, config: Config): String =
"<html>" + renderWidget(List("cart"), config) + "</html>"
def renderWidget(items: List[String], config: Config): String = ???
val config = Config(8080, "docs.scala-lang.org")
renderWebsite("/home", config)
Предположим, что конфигурация не меняется на протяжении большей части нашей кодовой базы.
Передача config
каждому вызову метода (например renderWidget
) становится очень утомительной
и делает нашу программу более трудной для чтения, поскольку нам нужно игнорировать аргумент config
.
Установка параметров как контекстных
Мы можем пометить некоторые параметры наших методов как контекстные.
def renderWebsite(path: String)(implicit config: Config): String =
"<html>" + renderWidget(List("cart")) + "</html>"
// ^
// аргумент config больше не требуется
def renderWidget(items: List[String])(implicit config: Config): String = ???
def renderWebsite(path: String)(using config: Config): String =
"<html>" + renderWidget(List("cart")) + "</html>"
// ^
// аргумент config больше не требуется
def renderWidget(items: List[String])(using config: Config): String = ???
Начав секцию параметров с ключевого слова using
в Scala 3 или implicit
в Scala 2, мы сообщаем компилятору,
что на стороне вызова он должен автоматически найти аргумент с необходимым типом.
Таким образом, компилятор Scala выполняет вывод термов.
При вызове renderWidget(List("cart"))
компилятор Scala увидит, что в области видимости есть терм типа Config
(в нашем случае - config
) и автоматически предоставит его для renderWidget
.
Таким образом, программа эквивалентна приведенной выше.
На самом деле, поскольку в реализации renderWebsite
больше не нужно ссылаться на config
,
мы можем даже опустить его имя в подписи в Scala 3:
// нет необходимости придумывать имя параметра
// vvvvvvvvvvvvv
def renderWebsite(path: String)(using Config): String =
"<html>" + renderWidget(List("cart")) + "</html>"
В Scala 2 именовать неявные параметры по-прежнему необходимо.
Явное указание контекстных параметров
Мы увидели, как абстрагироваться от контекстных параметров
и что компилятор Scala может автоматически предоставлять нам аргументы.
Но как мы можем указать, какую конфигурацию использовать для нашего вызова renderWebsite
?
Мы явно указываем значение аргумента, как если бы это был обычный аргумент:
renderWebsite("/home")(config)
Подобно тому, как мы указали наш раздел параметров с помощью using
,
мы также можем явно указать контекстные параметры с помощью using
:
renderWebsite("/home")(using config)
Явное предоставление контекстных параметров может быть полезно, когда у нас в области видимости есть несколько разных значений, подходящих по типу, и мы хотим убедиться в корректности передачи параметра методу.
Для всех остальных случаев, как мы увидим в следующем разделе, есть еще один способ ввести контекстуальные значения в область видимости.
Экземпляры given (определения implicit в Scala 2)
Мы видели, что можем явно передавать аргументы в качестве контекстных параметров.
Однако, если для определенного типа существует единственное каноническое значение,
есть другой предпочтительный способ сделать его доступным для компилятора Scala:
пометив его как given
в Scala 3 или implicit
в Scala 2.
implicit val config: Config = Config(8080, "docs.scala-lang.org")
// ^^^^^^
// это значение, которое выведет компилятор Scala
// в качестве аргумента контекстного параметра типа Config
val config = Config(8080, "docs.scala-lang.org")
// это тип, который мы хотим предоставить для канонического значения
// vvvvvv
given Config = config
// ^^^^^^
// это значение, которое выведет компилятор Scala
// в качестве аргумента контекстного параметра типа Config
В приведенном выше примере мы указываем, что всякий раз,
когда в текущей области видимости опущен контекстный параметр типа Config
,
компилятор должен вывести config
в качестве аргумента.
Определив каноническое значение для типа Config
,
мы можем вызвать renderWebsite
следующим образом:
renderWebsite("/home")
// ^
// снова без аргумента
Подробное руководство о том, где Scala ищет канонические значения, можно найти в FAQ.
Contributors to this page:
Contents
- Введение
- Возможности Scala
- Почему Scala 3?
- Почувствуй Scala
- Пример 'Hello, World!'
- REPL
- Переменные и типы данных
- Структуры управления
- Моделирование данных
- Методы
- Функции первого класса
- Одноэлементные объекты
- Коллекции
- Контекстные абстракции
- Верхнеуровневые определения
- Обзор
- Первый взгляд на типы
- Интерполяция строк
- Структуры управления
- Моделирование предметной области
- Инструменты
- Моделирование ООП
- Моделирование ФП
- Методы
- Особенности методов
- Main методы в Scala 3
- Обзор
- Функции
- Анонимные функции
- Параметры функции
- Eta расширение
- Функции высшего порядка
- Собственный map
- Создание метода, возвращающего функцию
- Обзор
- Пакеты и импорт
- Коллекции в Scala
- Типы коллекций
- Методы в коллекциях
- Обзор
- Функциональное программирование
- Что такое функциональное программирование?
- Неизменяемые значения
- Чистые функции
- Функции — это значения
- Функциональная обработка ошибок
- Обзор
- Типы и система типов
- Определение типов
- Параметризованные типы
- Пересечение типов
- Объединение типов
- Алгебраические типы данных
- Вариантность
- Непрозрачные типы
- Структурные типы
- Зависимые типы функций
- Другие типы
- Контекстные абстракции
- Методы расширения
- Параметры контекста
- Контекстные границы
- Given импорты
- Классы типов
- Многостороннее равенство
- Неявное преобразование типов
- Обзор
- Параллелизм
- Scala утилиты
- Сборка и тестирование проектов Scala с помощью Sbt
- Рабочие листы
- Взаимодействие с Java