В scala 2 или 3 можно ли отлаживать процесс неявного разрешения во время выполнения?
В языке Scala неявное разрешение часто выполняется во время компиляции и иногда выдает запутывающую информацию об ошибке, один известный пример такой ошибки - это когда shapeless Generic выдает информацию об ошибке, например:
error: could not find implicit value for parameter encoder: CsvEncoder[Foo]
(подробнее см. https://books.underscore.io/shapeless-guide/shapeless-guide.html)
Решение этой проблемы состоит в том, чтобы запустить алгоритм неявного разрешения (внутренний алгоритм должен быть алгоритмом запроса графа) во время выполнения, это дает как минимум 2 преимущества:
Инструменты отладки могут использоваться для пошагового воспроизведения процесса разрешения, поэтому даже если информация об ошибке и документация неполны, ошибку будет легко обнаружить.
во многих случаях информацию о типе невозможно определить во время компиляции (например, тип зависит от потока управления). Если неявное преобразование не может быть отложено до фазы выполнения, многие преимущества определения неявного преобразования будут сведены на нет.
Итак, мой вопрос: существует ли эта функция в Scala 2.x или Dotty? Или это в дорожной карте?
Большое спасибо за ваше мнение.
1 ответ
Вы можете отлаживать имплициты во время компиляции:
включить
-Xlog-implicits
попробуйте разрешить имплициты вручную (возможно, указав также параметры типа) и увидеть ошибки компиляции
implicitly[...](...manually...)
использовать
scala.reflect
println(reify { implicitly[...] }.tree)
использовать функциональность IDE, чтобы показать имплицит
используя макросы с внутренними компонентами компилятора, вы можете отлаживать неявное разрешение
Существует ли класс типа, который проверяет наличие хотя бы одного неявного типа?
создать неоднозначный неявный низкий приоритет
Использование "Пролога в Scala" для поиска доступных экземпляров классов типов
Нахождение второго неявного совпадения
https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/package.scala
Если вы разрабатываете класс типа, не забывайте использовать аннотации @implicitNotFound
а также @implicitAmbiguous
.
Вы всегда можете отложить компиляцию вашей программы на время выполнения. Так что вместо программы
object App {
def main(args: Array[String]): Unit = {
println("test") // test
}
}
вы можете иметь
import scala.reflect.runtime.currentMirror
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox
val toolbox = currentMirror.mkToolBox()
toolbox.eval(q"""
object App {
def main(args: Array[String]): Unit = {
println("test")
}
}
App.main(Array())
""") // test
И вместо
implicitly[Numeric[Int]]
вы можете иметь
toolbox.compile(q"""
implicitly[Numeric[Int]]
""")
или
toolbox.inferImplicitValue(
toolbox.typecheck(tq"Numeric[Int]", mode = toolbox.TYPEmode).tpe,
silent = false
)
Но слишком оптимистично думать, что, отложив компиляцию программы до времени выполнения, вы сможете легче отлаживать неявные последствия во время выполнения, а не во время компиляции. Фактически, откладывая компиляцию программы до времени выполнения, вы добавляете еще один уровень косвенности, т.е. затрудняете отладку.