scala - 如何在 Scala 编译时检查某个 T 是否为 case 类?

标签 scala scala-macros scala-2.11

我有以下宏:

package macros

import scala.reflect.macros.blackbox.Context

object CompileTimeAssertions {
  def mustBeCaseClass[T]: Unit =
    macro CompileTimeAssertionsImpl.mustBeCaseClass[T]
}

object CompileTimeAssertionsImpl {
  def mustBeCaseClass[T: c.WeakTypeTag](c: Context): c.Expr[Unit] = {
    import c.universe._
    val symbol = c.weakTypeTag[T].tpe.typeSymbol
    if (!symbol.isClass || !symbol.asClass.isCaseClass) {
      c.error(c.enclosingPosition, s"${symbol.fullName} must be a case class")
    }
    reify(Unit)
  }
}

它在不涉及泛型时有效,但在以下情况下失败:
import macros.CompileTimeAssertions._
import org.scalatest.{Matchers, WordSpec}

case class ACaseClass(foo: String, bar: String)

class NotACaseClass(baz: String)

class MacroSpec extends WordSpec with Matchers {
  "the mustBeCaseClass macro" should {
    "compile when passed a case class" in {
      mustBeCaseClass[ACaseClass]
    }

    "not compile when passed a vanilla class" in {
//      mustBeCaseClass[NotACaseClass] // fails to compile as expected.
    }

    "compile when working with generics" in {
//      class CaseClassContainer[T] { mustBeCaseClass[T] } // fails to compile.
//      new CaseClassContainer[ACaseClass]
    }
  }
}

编译器错误是我的:
MacroSpec.CaseClassContainer.T must be a case class

我想知道实例化 CaseClassContainer 时 T 是什么。这甚至可能吗?如果是你能提供一个例子吗?

提前致谢。

最佳答案

感谢 Eugene 和 Travis 的建议,我能够用类型类解决这个问题。这是解决方案:

package macros

import scala.reflect.macros.blackbox.Context

trait IsCaseClass[T]

object IsCaseClass {
  implicit def isCaseClass[T]: IsCaseClass[T] =
    macro IsCaseClassImpl.isCaseClass[T]
}

object IsCaseClassImpl {
  def isCaseClass[T]
      (c: Context)
      (implicit T: c.WeakTypeTag[T]): c.Expr[IsCaseClass[T]] = {
    import c.universe._
    val symbol = c.weakTypeTag[T].tpe.typeSymbol
    if (!symbol.isClass || !symbol.asClass.isCaseClass) {
      c.abort(c.enclosingPosition, s"${symbol.fullName} must be a case class")
    } else {
      c.Expr[IsCaseClass[T]](q"_root_.macros.IsCaseClassImpl[$T]()")
    }
  }
}

case class IsCaseClassImpl[T]() extends IsCaseClass[T]

这是用法:
import macros.IsCaseClass
import org.scalatest.{Matchers, WordSpec}

case class ACaseClass(foo: String, bar: String)

class NotACaseClass(baz: String)

class CaseClassContainer[T: IsCaseClass]

class MacroSpec extends WordSpec with Matchers {
  "the code" should {
    "compile" in {
      new CaseClassContainer[ACaseClass]
    }

    "not compile" in {
//      new CaseClassContainer[NotACaseClass]
    }
  }
}

值得注意的使用abort而不是 error .中止返回 Nothing而错误返回 Unit .当宏没有返回任何内容时,后者很好。

关于scala - 如何在 Scala 编译时检查某个 T 是否为 case 类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30233178/

相关文章:

arrays - 将 Range 直接映射到 Array

scala - 使用 Scala 宏在树中查找所有可能的序列创建

scala - "Error:scalac: Error: object VolatileFloatRef does not have a member create"使用 Scala 2.11.8 SDK 时

Java/Scala Kafka Producer 不向主题发送消息

Scala,关于方法签名的建议

json - Spark.RDD take(n) 返回元素为 n 的数组,n 次

scala - 如何将参数/设置传递给 Scala 宏?

scala - 检查Scala宏中的varargs类型归属

scala - 这可能是 Scala 编译器错误吗?