宏实现未找到(scala 2.13.3)

问题描述 投票:0回答:1

我在我的 Scala 代码中使用 RunningStrategies 类来定义一组运行策略。为了创建 RunningStrategies 的实例,我使用了 apply 方法以及代表不同策略的可变数量的参数。为此,我还定义了一个名为 RunningStrategiesMacros 的宏。

但是,当我尝试将 RunningStrategies 与某些策略一起使用时,我收到以下错误消息:“找不到宏实现:应用”。该错误似乎与我试图在定义它的同一编译运行中使用宏实现有关。

跑步策略:

package com.dv.phoenix.brandsafety.models

object RunningStrategy extends Enumeration {
  type RunningStrategy = Value

  val
  MonitoringOnly,
  BlockingOnly,
  MonitoringAndBlockingInherent,
  MonitoringAndBlockingRecalculate
  = Value
}

跑步策略:

package com.dv.phoenix.brandsafety.models

import com.dv.phoenix.brandsafety.models.RunningStrategy.RunningStrategy
import com.dv.phoenix.brandsafety.utils.RunningStrategiesMacros

import scala.language.experimental.macros
case class RunningStrategies private (runningStrategies: Set[RunningStrategy])
object RunningStrategies {
  def apply(strategies: RunningStrategy*): RunningStrategies = macro RunningStrategiesMacros.applyImp
}

RunningStrategiesMacro:

package com.dv.phoenix.brandsafety.utils

import com.dv.phoenix.brandsafety.models.RunningStrategies
import com.dv.phoenix.brandsafety.models.RunningStrategy.RunningStrategy

import scala.reflect.macros.blackbox

object RunningStrategiesMacros {
  def applyImp(c: blackbox.Context)(strategies: c.Expr[RunningStrategy]*): c.Expr[RunningStrategies] = {
    import c.universe._

    val strategySet = strategies.map(_.tree).toList
    if (strategySet.contains(q"MonitoringAndBlockingInherent") && strategySet.contains(q"MonitoringAndBlockingRecalculate")) {
      c.abort(c.enclosingPosition, "MonitoringAndBlockingInherent and MonitoringAndBlockingRecalculate cannot be used together")
    }

    val runningStrategies = q"RunningStrategies(Set(..$strategySet))"
    c.Expr[RunningStrategies](runningStrategies)
  }
}

用法:

override protected val runningStrategies: RunningStrategies = RunningStrategies(MonitoringOnly, MonitoringAndBlockingInherent, MonitoringAndBlockingRecalculate)

我收到以下错误:

macro implementation not found: apply
(the most common reason for that is that you cannot use macro implementations in the same compilation run that defines them)
  override protected val runningStrategies: RunningStrategies = RunningStrategies(MonitoringOnly, MonitoringAndBlockingInherent, MonitoringAndBlockingRecalculate)

我注意到 scala-logging 库已经设法用他们的 LoggerImplLoggerMacro 类解决了这个问题。有人可以解释他们是如何做到这一点的,以及我如何将相同的技术应用于我的 RunningStrategiesMacro?

scala scala-macros
1个回答
0
投票

在您的问题中,您描述了您的包结构,但没有描述您的项目结构。包和子项目是不同的概念

为什么sbt/assembly在有多个子项目的项目中找不到主类?

如果你想在 Scala 2 中使用 macros,你应该有不同的子项目

https://www.scala-sbt.org/1.x/docs/Macro-Projects.html

首先,让我们刷新一下术语。这是一个宏定义

import scala.language.experimental.macros

def foo(): Unit = macro fooImpl

这是一个宏实现

import scala.reflect.macros.blackbox

def fooImpl(c: blackbox.Context)(): c.Tree = {
  import c.universe._
  println("test")
  q"""_root_.scala.Predef.println("foo")"""
}

这是一个宏应用程序

foo()
// at compile time: scalac: test
// at runtime: foo

在 Scala 2 中,宏实现必须在宏应用程序之前编译(宏应用程序的编译时,即宏扩展是宏的运行时)。所以宏 implementations 和宏 applications 必须在不同的编译单元中。例如在您项目的不同子项目中。或者在同一项目的

src/main/scala
src/test/scala
中(主要代码在测试之前编译)。宏定义在哪里并不那么重要。它们可以在与宏实现相同的编译单元/子项目中(可能在相同的文件/类中)或与宏应用程序相同或在它们自己的中。

scala-logging
中,宏定义

def error(message: String): Unit = macro LoggerMacro.errorMessage

和宏实现

def errorMessage(c: LoggerContext)(message: c.Expr[String]): c.universe.Tree = ...

在同一个项目中(在不同的文件中但这不是必需的,它们可以在同一个文件中)但是宏应用程序

logger.error(msg)

src/test/scala
https://github.com/lightbend-labs/scala-logging/blob/v3.9.5/src/test/scala/com/typesafe/scalalogging/LoggerSpec.scala。其他宏应用程序也将在不同的编译单元中,即使用
scala-logging
作为依赖项的客户端项目。

在你的项目中

def apply(strategies: RunningStrategy*): RunningStrategies = macro RunningStrategiesMacros.applyImp

是宏定义并且

def applyImp(c: blackbox.Context)(strategies: c.Expr[RunningStrategy]*): c.Expr[RunningStrategies] = ...

是宏实现。但是你应该重新组织你的项目至少有两个子项目并放置宏应用程序

override protected val runningStrategies: RunningStrategies = RunningStrategies.apply(MonitoringOnly, MonitoringAndBlockingInherent, MonitoringAndBlockingRecalculate)

到不同的子项目。

是否有任何技巧可以在定义的同一文件中使用宏?

是否可以使用反射或类似方法识别/使用 Scala 宏?

使用 java 中的 scala 宏

同一模块中的隐式物化

© www.soinside.com 2019 - 2024. All rights reserved.