Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala宏注释-带有类型参数的case类_Scala_Macros_Scala 2.11_Scala Quasiquotes_Scala Macro Paradise - Fatal编程技术网

Scala宏注释-带有类型参数的case类

Scala宏注释-带有类型参数的case类,scala,macros,scala-2.11,scala-quasiquotes,scala-macro-paradise,Scala,Macros,Scala 2.11,Scala Quasiquotes,Scala Macro Paradise,我正在尝试为case类编写一个简单的宏注释,它向伴随对象添加了一个方法。关键是新方法必须考虑带注释的case类上的类型参数 这是我需要通过的测试 package my.macros import org.scalatest._ class DefaultApplyTest extends FlatSpec with Matchers { @defaultApply case class Generic[A, B](a: A, b: B) it should "define def

我正在尝试为case类编写一个简单的宏注释,它向伴随对象添加了一个方法。关键是新方法必须考虑带注释的case类上的类型参数

这是我需要通过的测试

package my.macros

import org.scalatest._

class DefaultApplyTest extends FlatSpec with Matchers {

  @defaultApply case class Generic[A, B](a: A, b: B)

  it should "define defaultApply method in companion object" in {
    assert(Generic.defaultApply("str", 1) == Generic("str", 1))
  }
}
下面是我为实现这一点而编写的代码

package my.macros

import scala.reflect.macros._
import scala.language.experimental.macros
import scala.annotation.StaticAnnotation

class defaultApply extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro DefaultApply.impl
}

object DefaultApply {

  def impl(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._

    def defaultApplyCompanion(classDecl: ClassDef) = {
      val (name, typeParams, valueParams) = try {
        val q"case class ${name: TypeName}[..${typeParams: Seq[TypeDef]}](..${valueParams: Seq[ValDef]}) extends ..$bases { ..$body }" = classDecl
        (name, typeParams, valueParams)
      } catch {
        case e: MatchError =>
          c.warning(c.enclosingPosition, e.toString)
          c.abort(c.enclosingPosition, "Annotation is only supported on case class")
      }

      val applyDef = q"""${name.toTermName}.apply[..$typeParams]"""
      c.warning(c.enclosingPosition, showRaw(applyDef))

      q"""
        object ${name.toTermName} {
          def defaultApply: (..${valueParams.map(_.tpt)}) => $name[..$typeParams] = $applyDef
        }
      """
    }

    def modifiedDeclaration(classDecl: ClassDef) = {
      val compDecl = defaultApplyCompanion(classDecl)

      c.Expr(q"""
        $classDecl
        $compDecl
      """)
    }

    annottees.map(_.tree) match {
      case (classDecl: ClassDef) :: Nil => modifiedDeclaration(classDecl)
      case _ => c.abort(c.enclosingPosition, "Invalid annottee")
    }
  }
}
据我所知,问题在于,当我尝试将类型参数列表添加到生成的语法树中时,它们不会被识别为与原始树中的类型参数相同的类型参数

所以我关注的是,对于宏的这一部分

  val applyDef = q"""${name.toTermName}.apply[..$typeParams]"""
  c.warning(c.enclosingPosition, showRaw(applyDef))
原始语法树作为

TypeApply(Select(Ident(TermName("Generic")), TermName("apply")), List(TypeDef(Modifiers(PARAM), TypeName("A"), List(), TypeBoundsTree(EmptyTree, EmptyTree)), TypeDef(Modifiers(PARAM), TypeName("B"), List(), TypeBoundsTree(EmptyTree, EmptyTree))))
但是编译器对此不满意

type arguments [<notype>,<notype>] do not conform to method apply's type parameter bounds [A,B]
类型参数[,]不符合方法apply的类型参数界限[A,B]
最终的用例是生成可缓存类型类的实例,该类涉及1k行代码。非参数化版本已经可以工作了,这只是锦上添花。在scalac的引擎盖下有一些东西我不明白,但我想知道。非常感谢您花时间阅读本文


我将Scala 2.11.8与

一起使用,问题似乎是使用类型参数代替类型参数。这似乎有效(我还必须将类型参数添加到
defaultApply
方法声明中):

val-typeArgs=typeParams.map(\u.name)
val applyDef=q“”“${name.toTermName}.apply[..$typeArgs]”
c、 警告(c.封闭位置,showRaw(applyDef))
q”“”
对象${name.toTermName}{
def defaultApply[…$typeParams]:
(..${valueParams.map(u.tpt)})=>$name[..$typeArgs]=$applyDef
}
"""