【问题标题】:Searching for implicit inside a reify call (scala macro)在 reify 调用中搜索隐式(scala 宏)
【发布时间】:2019-09-12 09:40:35
【问题描述】:

我需要在给定位置搜索隐式值。我保留了类中先前宏调用的位置,如下所示:

class Delayed[+Kind[_[_]]](val sourceFilePath: String, val callSitePoint: Int) {
  def find[F[_]]: Kind[F] = macro Impl.find[Kind, F]
}

前面的宏很简单:

def build[Kind[_[_]]](c: blackbox.Context): c.Expr[Delayed[Kind]] = {
    import c.universe._

    c.Expr(
      q"""
        new Delayed(${c.enclosingPosition.point}, ${c.enclosingPosition.source.path})
       """
    )
  }

有了这个位置,我需要做的就是启动隐式搜索吗?

def find[Kind[_[_]], F[_]](c: blackbox.Context)(implicit kindTag: c.WeakTypeTag[Kind[F]], fTag: c.WeakTypeTag[F[_]]): c.Expr[Kind[F]] = {
    import c.universe._

    reify {
      val self = c.prefix.splice.asInstanceOf[Delayed[Kind]]
      val sourceFile = AbstractFile.getFile(self.sourceFilePath)
      val batchSourceFile = new BatchSourceFile(sourceFile, sourceFile.toCharArray)
      val implicitSearchPosition = new OffsetPosition(batchSourceFile, self.callSitePoint).asInstanceOf[c.Position]

      c.Expr[Kind[F]](c.inferImplicitValue(
        appliedType(kindTag.tpe.typeConstructor, fTag.tpe.typeConstructor),
        pos = implicitSearchPosition
      )).splice
    }
  }

我使用 reify/splice 调用获得了职位,然后申请 inferImplicitValue。但是编译器抱怨隐式值的最后一个拼接:

the splice cannot be resolved statically, 
which means there is a cross-stage evaluation involved

它要求我将编译器 jar 添加为依赖项,但这样做我只会得到另一个错误:

Macro expansion contains free term variable c defined by find in Delayed.scala

我知道,从概念上讲,reify 是在价值观的世界中。我不明白的是,在将宏生成的代码写入我的源代码之前,应该先解决隐式搜索。这是我能想到的让隐式搜索在宏上下文中工作的唯一方法。

我哪里错了?我确实理解编译器消息,但对我来说,在这个特定的上下文中没有任何意义。也许我不明白inferImplicitValue 的工作原理。

【问题讨论】:

    标签: scala implicit scala-macros type-level-computation


    【解决方案1】:

    试试Context#eval(expr)

    def find[Kind[_[_]], F[_]](c: blackbox.Context)(implicit kindTag: c.WeakTypeTag[Kind[F]], fTag: c.WeakTypeTag[F[_]]): c.Expr[Kind[F]] = {
      import c.universe._
    
      val self = c.eval(c.Expr[Delayed[Kind]](c.untypecheck(c.prefix.tree.duplicate)))
      val sourceFile = AbstractFile.getFile(self.sourceFilePath)
      val batchSourceFile = new BatchSourceFile(sourceFile, sourceFile.toCharArray)
      val implicitSearchPosition = new OffsetPosition(batchSourceFile, self.callSitePoint).asInstanceOf[c.Position]
    
      c.Expr[Kind[F]](c.inferImplicitValue(
        appliedType(kindTag.tpe.typeConstructor, fTag.tpe.typeConstructor),
        pos = implicitSearchPosition
      ))
    }
    

    【讨论】:

    • 使用您的代码,我在第一行收到以下错误:java.lang.IndexOutOfBoundsException: 0 经过大量阅读后,似乎我的错误是使用c.prefix。我认为它是Delayed 实例的thisself。它似乎有点不同,取决于宏的调用站点。你知道如何随时指向Delayed 的“this”吗? (当然感谢您的帮助)
    • @Merlin 您提供的信息不足,无法复制IndexOutOfBoundsException
    • @Merlin 如果将println(c.prefix.tree) 添加到宏中,则object App { trait Kind[_[_]]; trait F[_]; implicit val kindF: Kind[F] = new Kind[F] {}; new Delayed[Kind]("some existing file", 0).find[F] } 的编译将打印Warning:scalac: new App.Delayed[App.Kind]("some existing file", 0)。所以c.prefix 就像this
    • 似乎是在存储 delayed 实例并传递它时。现在信息还不够,下班后我会继续搜索。
    • 如果我稍微改变一下你的例子:val tmp = new Delayed[Kind](...); tmp.find[F] 在这种情况下,它将打印YourApp.type.tmp 作为前缀。这显然不是我的意思。
    猜你喜欢
    • 2019-10-02
    • 1970-01-01
    • 2012-11-27
    • 2015-05-07
    • 1970-01-01
    • 2017-03-12
    • 2021-01-29
    • 2011-05-25
    • 1970-01-01
    相关资源
    最近更新 更多