Использование freshName в качестве параметра без явного указания типа

#scala #metaprogramming #scala-macros #scala-quasiquotes

#scala #метапрограммирование #scala-макросы #scala-квазиквоты

Вопрос:

Я пытаюсь использовать 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
 

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()} без явного указания типа параметра?

Ответ №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 ?> внимание, какой тип является пустым.