Comments 11
Используя implicit аргументы функций можно наворотить крайне запутанный код на ровном месте. Один из тех случаев, когда "можно", но лучше не надо.
С учетом этого, считаю недостатком, что в статье не описаны best-practices по использованию этой фичи языка.
Вот тут сам автор языка очень хорошо описывает основные случаи использования (осторожно, антимонгольский): https://youtu.be/br6035SKu-0
Используются они направо и налево авторами библиотек безо всякой нужды, просто потому что им лень добавить явный параметр или член класса. При этом код становится абсолютно запутанным и сложно переиспользуемым — теперь недостаточно просто скопировать откуда-то (из документации, со стековерфлоу) пару строчек кода использования библиотечной функции, надо еще чертыхаясь лазать по мануалам, вычитывать эти имплисит параметры, а потом придумывать откуда их взять. Так как их инициализацию авторы фрагментов кода часто опускают.
Экономия на количестве кода при этом грошевая, а удар по читабельности огромный.
Неявные преобразования — тоже тот еще способ выстрелить себе в ногу, их полезность по сути только в том, чтобы можно было добавить один магический импорт и бесплатно получить конвертацию между джава и скала классами. Ведь своего богатого набора библиотек у скалы нет, приходится пользоваться джавовскими, а даже базовые классы (списки всякие и прочие популярные структуры данных) у них не совместимы. В итоге чтобы использовать джава-код надо либо вручную писать сотни бойлерплейта по конвертации одних коллекций в другие, либо вот использовать иплиситы как костыль.
Да, функционал одновременно и рискованный и мощный. Многие варианты использования имплиситов неуместны, а неявные преобразования и вовсе будут исключены из языка. С другой стороны этот механизм позволяет реализовать очень интересный механизм — автоматический вывод структур. Например если существует json формат для чисел и строк, и показано как строить форматы для списков и мап, то формат для Map[String, Seq[Int]]
выводится автоматически и на стадии компиляции. При этом это не вшито в библиотеку json, а работает для любых типов.
их полезность по сути только в том, чтобы можно было добавить один магический импорт и бесплатно получить конвертацию между джава и скала классами
Иногда хочется добавить метод к чужому классу. К примеру, штатно коллекции имеют методы min/max, но для пустых коллекций они бросают исключение. А с неявными преобразованиями нужные методы будто всегда жили в коллекциях.
scala> implicit final class SeqMaxOption[T](val data: Seq[T]) extends AnyVal {
| def maxOption(implicit cmp: Ordering[T]): Option[T] = if (data.isEmpty) None else Some(data.max)
| def minOption(implicit cmp: Ordering[T]): Option[T] = if (data.isEmpty) None else Some(data.max)
| }
defined class SeqMaxOption
scala> List[Int]().minOption
res5: Option[Int] = None
scala> List(1,2,3).minOption
res6: Option[Int] = Some(3)
Без неявного параметра упадёт с ошибкой:
def double(implicit value: Int) = value * 2 println(double) // error: could not find implicit value for parameter value
Упадёт в рантайме или всё таки не скомпилируется?
И по поводу синтаксиса: неужели нет способа сделать неявным только первый параметр?
Стоит отметить, что в Dotty/Scala 3.0 механизм и синтаксис имплиситов сильно переработан. То, что раньше делали имплиситы, теперь называется contextual abstractions, и разбито на несколько ортогональных концепций: https://dotty.epfl.ch/docs/reference/contextual/motivation.html
Неявные (implicit) параметры и преобразования в Scala