代码之家  ›  专栏  ›  技术社区  ›  Some Name

使用freshName作为参数,但不显式指定类型

  •  0
  • Some Name  · 技术社区  · 5 年前

    我试图在以下宏中使用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
    

    二、

    用硬编码标识符替换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()} 不显式指定参数类型?

    0 回复  |  直到 4 年前
        1
  •  1
  •   Some Name    5 年前

    解决方案:

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

    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 ?> 这是一个空类型。

    推荐文章