首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在reify调用(scala宏)中搜索隐式

在reify调用(scala宏)中搜索隐式
EN

Stack Overflow用户
提问于 2019-04-23 08:33:54
回答 1查看 239关注 0票数 0

我需要在给定的位置寻找一个隐含的值。我在类中保留了以前宏调用中的位置,如下所示:

代码语言:javascript
复制
class Delayed[+Kind[_[_]]](val sourceFilePath: String, val callSitePoint: Int) {
  def find[F[_]]: Kind[F] = macro Impl.find[Kind, F]
}

前面的宏非常简单:

代码语言:javascript
复制
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})
       """
    )
  }

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

代码语言:javascript
复制
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。但是编译器抱怨隐式值上的最后一个剪接:

代码语言:javascript
复制
the splice cannot be resolved statically, 
which means there is a cross-stage evaluation involved

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

代码语言:javascript
复制
Macro expansion contains free term variable c defined by find in Delayed.scala

我明白,在概念上,具体化是在价值的世界里。我不明白的是,在将宏生成的代码写入我的源代码之前,应该解析隐式搜索。这是我唯一能想到的让隐式搜索在宏上下文中工作的方法。

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

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-04-23 09:46:26

试试Context#eval(expr)

代码语言:javascript
复制
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
  ))
}

或者,在计算前缀定义之前,您可以尝试找到前缀定义的右侧:

macros__/src/main/scala/Delayed.scala

代码语言:javascript
复制
import scala.language.experimental.macros
import scala.reflect.internal.util.{BatchSourceFile, OffsetPosition}
import scala.reflect.io.AbstractFile
import scala.reflect.macros.whitebox

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

class Impl(val c: whitebox.Context) {
  import c.universe._

  def build[Kind[_[_]]](implicit kindTag: c.WeakTypeTag[Kind[Any]/*[F] forSome {type F[_]}*/]): c.Expr[Delayed[Kind]] = {
    c.Expr[Delayed[Kind]](
      q"""
        new Delayed[${kindTag.tpe.typeConstructor}](${c.enclosingPosition.source.path}, ${c.enclosingPosition.point})
      """
    )
  }

  def find[Kind[_[_]], F[_]](implicit kindTag: c.WeakTypeTag[Kind[Any]], fTag: c.WeakTypeTag[F[_]]): c.Expr[Kind[F]] = {
    val prefix       = c.prefix.tree
    val prefixSymbol = prefix.symbol

    def eval[A: WeakTypeTag](tree: Tree): Either[Throwable, A] = {
      // import org.scalamacros.resetallattrs._ // libraryDependencies += "org.scalamacros" %% "resetallattrs" % "1.0.0" // https://github.com/scalamacros/resetallattrs
      // util.Try(c.eval(c.Expr[A](c.resetAllAttrs(tree.duplicate)))).toEither
      util.Try(c.eval(c.Expr[A](c.untypecheck(c.typecheck(tree/*.duplicate*/))))).toEither // see (*) below
    }

    val self: Delayed[Kind] = eval[Delayed[Kind]](prefix).orElse {

      var rhs: Either[Throwable, Tree] = Left(new RuntimeException(s"can't find RHS of definition of $prefix"))

      val traverser = new Traverser {
        override def traverse(tree: Tree): Unit = {
          tree match {
            case q"$_ val $_: $_ = $expr"
              if tree.symbol == prefixSymbol ||
                (tree.symbol.isTerm && tree.symbol.asTerm.getter == prefixSymbol) =>
              rhs = Right(expr)
            case _ =>
              super.traverse(tree)
          }
        }
      }

      c.enclosingRun.units.foreach(unit => traverser.traverse(unit.body))

      rhs.flatMap(eval[Delayed[Kind]])

    }.fold(err => c.abort(c.enclosingPosition, s"can't find or eval self because: $err"), identity)

    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),
      silent = false,
      pos = implicitSearchPosition
    ))
  }
}

但我怀疑具有指定位置的c.inferImplicitValue不像您预期的那样工作。我猜该位置用于错误消息,而不是用于管理隐式解析范围。如果您想以这种方式处理隐式解析的优先级,可以使用编译器内部(1 2 3. 4. 5)。

macros__/src/main/scala/Functor.scala

代码语言:javascript
复制
trait Functor[F[_]]

core__/src/main/scala/App.scala

代码语言:javascript
复制
object A {
  implicit val f: Functor[List] = new Functor[List] {}

  val d: Delayed[Functor] = Delayed.build[Functor]
}

object App {
  implicit val f1: Functor[List] = new Functor[List] {}

  A.d.find[List] // scalac: App.this.f1 // not A.f
}

斯卡拉2.13.10

从一个值传递参数

当提供分配给val的lambda时,获取scala.MatchError: F(类scala.reflect.internal.Trees$Ident)

从方法符号和主体创建方法定义树

Scala宏如何使用参数默认值将MethodSymbol转换为DefDef?

如何获得传递给Scala宏的参数的运行时值?

Scala:在Context.eval引用中可以编写什么代码?

如何从Scala 3宏的外部范围获取变量初始化的主体? (Scala 3)

(*) .duplicate实际上是不必要的:https://github.com/readren/json-facile/pull/1#issuecomment-733886784

原则上,在此阶段计算的值可以通过序列化/反序列化(允许一种跨阶段评估)保留到下一阶段。

如何在先前定义的对象中使用拟引号

但是序列化c: Context是不可能的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55807221

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档