[Программирование, Scala] Неявный вывод в Scala
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Многие начинающие и не очень Scala разработчики принимают implicits как умеренно полезную возможность. Использование обычно ограничивается передачей ExecutionContext во Future. Другие же избегают неявного и считают возможность вредной.Код же вроде этого вообще многих пугает:
implicit def function(implicit argument: A): B
Но я считаю этот механизм важным преимуществом языка, давайте разберемся почему.Кратко про implicitsВ целом implicits это механизм автоматического дополнения кода при компиляции:
- для неявных аргументов автоматически подставляется значение
- для неявных преобразований значение автоматически оборачивается в вызов метода
Я не буду углубляться в эту тему, кому интересно посмотрите об этом видео от создателя языка. Вкратце один этот механизм используется в нескольких разных случаях (и в Scala 3 будет разделен на несколько отдельных возможностей):
- Передача неявного контекста (как ExecutionContext)
- Неявные преобразования (не рекомендуется к использованию)
- Методы-расширения (extension methods, синтаксический сахар по добавлению методов к существующим типам)
- Классы типов (type classes) и неявный вывод (implicit resolution)
Классы типов и неявный выводСами по себе классы тыпов не несут какой-то новизны или революции – это попросту реализация методов "для" типа, а не "в самом" типе, это как разница между Comparable & Ordering(Comparator в Java):Comparable реализуется для добавления возможности сравнения в ООП стиле:
class Person(age: Int) extends Comparable[Person] {
override def compareTo(o: Person): Int = age compareTo o.age
}
def max[T <: Comparable[T]](xs: Iterable[T]): T = xs.reduce[T] {
case (a, b) if (a compareTo b) < 0 => b
case (a, _) => a
}
Ordering реализуется с той же целью, но отдельно от самого типа:
case class Person(age: Int)
implicit object ByAgeOrdering extends Ordering[Person] {
override def compare(o1: Person, o2: Person): Int = o1.age compareTo o2.age
}
def max[T: Ordering](xs: Iterable[T]): T = xs.reduce[T] {
case (a, b) if Ordering[T].lt(a, b) => b
case (a, _) => a
}
// is syntactic sugar for
def max[T](xs: Iterable[T])(implicit evidence: Ordering[T]): T = ...
Вот и весь класс типов.Интересным этот механизм делает неявный вывод. Вернемся к непонятному объявлению из начала статьи и добавим к нему разные комбинации:
implicit val value: A = ???
implicit def definition: B = ???
implicit def conversion(argument: C): D = ???
implicit def function(implicit argument: E): F = ???
В чем разница? Исключая conversion, который является неявным преобразованием, почти ни в чем: value, definition & function могут быть использованы чтобы подставить значение для неявного аргумента нужного типа. Разница только в способе вычисления: val статичен, а def вычисляется каждый раз при постановке. Неявный аргумент в такой функции работает как обычно – требует неявного значения в скоупе.Неявная функция с неявным аргументом дает нам довольно интересные возможности – выходит компилятор может не просто подставить неявное значение в нужное место, но и скомбинировать несколько вызовов для получения нужного значения, подобрав подходящие функции.Рассмотрим пример – мы можем описать порядок пар любых типов, для этого не нужно знать их заранее – достаточно знать, что у них тоже есть порядок:
implicit def pairOrder[A: Ordering, B: Ordering]: Ordering[(A, B)] = {
case ((a1, b1), (a2, b2)) if Ordering[A].equiv(a1, a2) => Ordering[B].compare(b1, b2)
case ((a1, _), (a2, _)) => Ordering[A].compare(a1, a2)
}
// again, just syntactic sugar for:
implicit def pairOrder[A, B](implicit a: Ordering[A], b: Ordering[B]): Ordering[(A, B)] = ...
Теперь если вдруг нам понадобится найти максимум среди сложных структур компилятор сам построит подходящий объект, комбинируя пользовательские и стандартные объявления:
val values = Seq(
(Person(30), ("A", "A")),
(Person(30), ("A", "B")),
(Person(20), ("A", "C"))
)
max(values) // => (Person(30),(A,B))
У нас был список типа Seq[(Person, (String, String))] и компилятор смог сам подобрать комбинацию функций для построения Ordering для этого типа:
max(values)(
pairOrder(
ByAgeOrdering,
pairOrder(Ordering.String, Ordering.String)
)
)
Так неявный вывод позволяет описывать общие правила вывода и поручить компилятору свести эти правила вместе и получить конкретную имплементацию класса типов. Добавляя собственный тип или собственные правила не нужно описывать все с начала – компилятор скомбинирует все сам, чтобы получить нужный объект.А главное, если компилятору это не удастся – вы получите ошибку компиляции, а не времени выполнения и сможете исправить проблему сразу на месте. Хотя конечно в бочке меда не без ложки дегтя – если у компилятора не вышло, вы не знаете какого звена в цепочке не хватило – дебажить такое не всегда просто.Надеюсь неявное стало теперь немного более явным.
===========
Источник:
habr.com
===========
Похожие новости:
- [Программирование, Assembler, Разработка игр, Математика] Попиксельная заливка экрана в Wolfenstein 3D (FizzleFade) — свежий взгляд
- [Разработка веб-сайтов, Программирование, Haskell, Функциональное программирование] Создаем веб-приложение на Haskell с использованием Reflex. Часть 2
- [Программирование] Новые направления развитии ИТ отрасли 2021
- [Ненормальное программирование, Программирование, Софт] Я пользуюсь Excel, чтобы писать код (перевод)
- [Программирование, C++, Git, Qt] QGit, улучшения
- [Программирование, Анализ и проектирование систем, Проектирование и рефакторинг, Микросервисы] Разложение монолита: Декомпозиция БД (часть 2)
- [Scala, Браузеры, Тестирование веб-сервисов] Scala + Selenium. Сколько человек в сборной имеют более одного гражданства?
- [Программирование, Assembler] Преобразование строки символов в число двойной точности (double)
- [Настройка Linux, Программирование, C, Rust, Разработка под Linux] Линус Торвальдс рассказал о том, где Rust впишется в Linux
- [Open source, Программирование, Системное программирование, Компиляторы, Rust] Rust 1.51.0: const generics MVP, новый распознаватель функциональности Cargo (перевод)
Теги для поиска: #_programmirovanie (Программирование), #_scala, #_scala, #_type_class, #_implicit, #_programmirovanie (
Программирование
), #_scala
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 20:48
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Многие начинающие и не очень Scala разработчики принимают implicits как умеренно полезную возможность. Использование обычно ограничивается передачей ExecutionContext во Future. Другие же избегают неявного и считают возможность вредной.Код же вроде этого вообще многих пугает: implicit def function(implicit argument: A): B
class Person(age: Int) extends Comparable[Person] {
override def compareTo(o: Person): Int = age compareTo o.age } def max[T <: Comparable[T]](xs: Iterable[T]): T = xs.reduce[T] { case (a, b) if (a compareTo b) < 0 => b case (a, _) => a } case class Person(age: Int)
implicit object ByAgeOrdering extends Ordering[Person] { override def compare(o1: Person, o2: Person): Int = o1.age compareTo o2.age } def max[T: Ordering](xs: Iterable[T]): T = xs.reduce[T] { case (a, b) if Ordering[T].lt(a, b) => b case (a, _) => a } // is syntactic sugar for def max[T](xs: Iterable[T])(implicit evidence: Ordering[T]): T = ... implicit val value: A = ???
implicit def definition: B = ??? implicit def conversion(argument: C): D = ??? implicit def function(implicit argument: E): F = ??? implicit def pairOrder[A: Ordering, B: Ordering]: Ordering[(A, B)] = {
case ((a1, b1), (a2, b2)) if Ordering[A].equiv(a1, a2) => Ordering[B].compare(b1, b2) case ((a1, _), (a2, _)) => Ordering[A].compare(a1, a2) } // again, just syntactic sugar for: implicit def pairOrder[A, B](implicit a: Ordering[A], b: Ordering[B]): Ordering[(A, B)] = ... val values = Seq(
(Person(30), ("A", "A")), (Person(30), ("A", "B")), (Person(20), ("A", "C")) ) max(values) // => (Person(30),(A,B)) max(values)(
pairOrder( ByAgeOrdering, pairOrder(Ordering.String, Ordering.String) ) ) =========== Источник: habr.com =========== Похожие новости:
Программирование ), #_scala |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 20:48
Часовой пояс: UTC + 5