2

在 Scala 语言中,隐式解析通常在编译时完成,有时会引发混淆错误信息,此类错误的一个著名示例是当无形 Generic 引发错误信息时,例如:

error: could not find implicit value for parameter encoder: CsvEncoder[Foo]

(详见https://books.underscore.io/shapeless-guide/shapeless-guide.html

解决这个问题的方法是在运行时运行隐式解析算法(内部应该是图查询算法),这至少有两个好处:

  • 调试工具可用于逐步重现解决过程,因此即使错误信息和文档不完整,也很容易发现错误。

  • 在许多情况下,类型信息可能无法在编译时确定(例如,类型取决于控制流)。如果隐式转换不能延迟到运行阶段,定义隐式转换的许多好处将无效。

所以我的问题是,Scala 2.x 或 Dotty 中是否存在此功能?还是在路线图上?

非常感谢您的意见。

4

1 回答 1

9

您可以在编译时调试隐式:

  1. 打开-Xlog-implicits

  2. 尝试手动解析隐式(也许也指定类型参数)并查看编译错误

    implicitly[...](...manually...)
    
  3. 利用scala.reflect

    println(reify { implicitly[...] }.tree)
    
  4. 使用 IDE 功能显示隐式

  5. 使用带有编译器内部的宏,您可以调试隐式解析

    是否有一个类型类来检查是否存在至少一个隐式类型?

    创建一个模棱两可的低优先级隐式

    使用“Prolog in Scala”查找可用的类型类实例

    找到第二个匹配的隐式

    https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/package.scala#L119-L168

如果您正在开发类型类,请不要忘记使用注解@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
)

但是,如果认为将程序编译推迟到运行时,您将能够在运行时而不是在编译时更容易地调试隐式,那就太乐观了。实际上将程序编译推迟到运行时,您会增加一层间接性,即使事情更难调试。

于 2019-12-16T01:19:50.210 回答