У Scala есть несколько вариантов использования, которые могут служить разным целям. В этой статье мы рассмотрим несколько примеров и попытаемся понять, чем они могут быть полезны. Мы покроем:

  • Неявные параметры
  • Преобразования типов (неявные функции)
  • «Прокачай мою библиотеку» (неявные классы)
  • Классы типов (неявные объекты)

Случай 1: неявные параметры

Давайте посмотрим на простейший пример

Здесь bob будет неявно передан в функцию greet. Отсутствующие параметры для вызова функции ищутся по типу в текущей области, что означает, что код не будет компилироваться, если в области нет неявной переменной типа String или есть несколько переменных одного типа, которые вызовет двусмысленность:

Я не думаю, что это вариант использования имплицитов с отступом, и я бы не рекомендовал его использовать по очевидным причинам.

Случай 2: преобразование типов с неявными функциями

Неявные функции позволяют нам определять преобразования между типами:

Когда компилятор видит тип, который не ожидается в контексте оценки, он попытается найти неявную функцию в текущей области, которая может создать ожидаемый тип. В нашем примере такими контекстами являются выражение 42.toUpperCase() и вызов функции functionTakingString(42). ФункцияtoUpperCase() не определена для целых чисел, поэтому intToStr рассматривается как преобразование и компиляция кода. Неявное имя функции не так важно - только подпись типа функции, в нашем случае это (Int) => (String).

Кейс 3: «Прокачай мою библиотеку»

Итак, как мы видели выше, неявная функция может преобразовывать некоторый тип A в тип B. Фактически нет ограничений на тип B, он не обязательно должен быть примитивным типом, как в примере. Допустим, у нас есть простой класс, работающий со строкой:

Мы можем написать неявную функцию, которая преобразует String в наш StringOps.

Это позволяет нам вызывать наши функции на String, как если бы они были частью String класса.

В Scala 2.10 представлены неявные классы, которые могут помочь нам уменьшить шаблон написания неявных функций для преобразования .

Обратите внимание, что существуют требования к тому, чтобы класс был неявным:

  • Он должен быть внутри другого признака, класса или объекта.
  • Он должен иметь ровно один параметр (но сам по себе может иметь несколько неявных параметров)
  • В области не может быть метода, члена или объекта с тем же именем.

Случай 4: Типовые классы

С помощью неявных объектов можно реализовать классы типов - конструкцию системы типов, которая поддерживает специальный полиморфизм. (Чтобы получить больше информации о классах типов и их назначении, перейдите по этой ссылке).

Класс типа чем-то похож на интерфейс, который может иметь несколько реализаций. В языках ООП эти реализации обычно представляют собой классы, которые расширяют интерфейс и создаются там, где это необходимо. С классами типов они должны быть созданы один раз и быть доступны «глобально». Синглтон - это обычное имя для этого шаблона, который scala изначально поддерживает с объявлениями object.

Типичный пример применения типовых классов - реализация Моноид.

Здесь мы используем неявные объекты, которые в основном представляют собой одиночные объекты, которые можно использовать в списке неявных параметров. Давайте посмотрим на функцию сумма: она берет последовательность некоторых значений и производит их сумму, но сумма может означать разные вещи в зависимости от типов значений. Если это целые числа, то это просто сложение, если строки - конкатенация строк, списки - конкатенация списков. Информация о том, какую реализацию использовать, содержится в неявном параметре, который обычно называется ev. ev означает свидетельство - свидетельство того, что предоставленный тип A реализует интерфейс Monoid. Было бы проще думать о свидетельствах как о функциональной аналогии паттерна стратегия, когда мы передаем желаемую реализацию в функцию. Мы также делаем это неявно, имея в виду, что компилятор сделает всю работу за вас. Если у вас нет реализации для какого-то типа и вы пытаетесь ее использовать - код не скомпилируется.

Существует альтернативный синтаксис для указания списка параметров агрегата:

Оба определения эквивалентны, но во втором случае обозначения немного короче. Но мы потеряли название свидетельства (реализации), на которое мы ссылаемся . Для его получения используется синтаксический сахар - implicitly:

Никакого волшебства здесь нет - implicitly - это обычная функция в Predef.scala, которая в основном принимает один неявный параметр, дает ему имя и возвращает его. Выглядит так:

Имплициты Scala - это мощные возможности языка, которые можно использовать в разных контекстах для достижения разных целей. Само собой разумеется, что из-за его неявного характера легко ошибиться, поэтому используйте его осторожно.