2

我正在尝试在以下宏中使用 freshName 作为参数名称:

我。

def test: Unit = macro implTst

def implTst(c: blackbox.Context): c.Expr[Unit] = {
  import c.universe._

  def withImplicitsM(exprs: List[c.Tree], expr: c.Tree): c.Tree =
    exprs match {
      case Nil =>
        expr
      case head :: tail =>
        //error here
        q"""$head.flatMap(implicit ${c.freshName()} => ${withImplicitsM(tail, expr)})"""

    }

  val exprsIo    = List(q"cats.effect.IO.apply(1)", q"cats.effect.IO.apply(2)")
  val resultTree = q"""println(${withImplicitsM(exprsIo, q"cats.effect.IO.apply(3)")}.unsafeRunSync())"""

  c.Expr[Unit](resultTree)
}

它抛出编译错误:

[error] Main.scala:25:9: exception during macro expansion: 
[error] java.lang.IllegalArgumentException: "fresh$macro$2" is not valid representation of a parameter, consider reformatting it into q"val $name: $T = $default" shape

二、

用硬编码标识符替换新名称使其工作:

def test: Unit = macro implTst

def implTst(c: blackbox.Context): c.Expr[Unit] = {
  import c.universe._

  def withImplicitsM(exprs: List[c.Tree], expr: c.Tree): c.Tree =
    exprs match {
      case Nil =>
        expr
      case head :: tail =>
        q"""$head.flatMap(implicit i => ${withImplicitsM(tail, expr)})"""

    }

  val exprsIo    = List(q"cats.effect.IO.apply(1)", q"cats.effect.IO.apply(2)")
  val resultTree = q"""println(${withImplicitsM(exprsIo, q"cats.effect.IO.apply(3)")}.unsafeRunSync())"""

  c.Expr[Unit](resultTree)
}

有没有办法在implicit ${c.freshName()}不明确指定参数类型的情况下使用?

4

1 回答 1

1

解决方案:

在参数定义中显式使用空类型。

def withImplicitsM(exprs: List[c.Tree], expr: c.Tree): c.Tree =
  exprs match {
    case Nil =>
      expr
    case head :: tail =>
      val emptyType = tq""
      val v         = q"implicit val ${TermName(c.freshName())}: $emptyType"
      q"""$head.flatMap($v => ${withImplicitsM(tail, expr)})"""

  }

我是怎么想出来的

我解构了一个类似的flatMap调用,并查看了参数定义的样子:

  val flatmapExpression               = q"cats.effect.IO.apply(1).flatMap(implicit i => cats.effect.IO.apply(2))"
  val q"$foo($args)"                  = flatmapExpression
  val q"(..$params) => $body"         = args
  val q"$mods val $name: $tpt = $rhs" = params(0)
  println(mods)
  println(name)
  println(tpt)
  println(rhs)

这是打印出来的:

Modifiers(implicit <param>, , Map())
i
<type ?>
<empty>

注意<type ?>这是一个空类型。

于 2020-12-15T09:00:10.627 回答