我试图在以下宏中使用freshName作为参数名称:
I
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
II
用硬编码的标识符替换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 =>
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()}
解决方案:
在参数定义中显式使用空类型。
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>
注意作为空类型的CCD_ 3。