我制作了一个枚举宏,最终出现了一种奇怪的行为。
这是主要的实施方式:
object Enum {
def values[A]: Seq[A] = macro Impl.values[A]
class Impl(val c: Context) {
import c.universe._
def values[A: c.WeakTypeTag]: c.Expr[A] = {
val elemType = weakTypeOf[A]
val elemSymbol = elemType.typeSymbol.asSealedClass
val elemSymbols = elemSymbol.knownDirectSubclasses.toList
val elemIdents = elemSymbols.map(s => Ident(s.asCaseClass.asModuleClass.module))
val elemSeqSymbol = weakTypeOf[Seq[A]].typeSymbol
c.Expr(Apply(Ident(elemSeqSymbol.companion), elemIdents))
}
implicit class SymbolOp(s: Symbol) {
def asSealedClass = s.asClass.ensuring(_.isSealed, s"$s is not sealed")
def asCaseClass = s.asClass.ensuring(_.isCaseClass, s"$s is not a case class")
def asModuleClass = s.asClass.ensuring(_.isModuleClass, s"$s is not an object")
}
}
}
这是测试:
object EnumTest extends App {
sealed trait Foo
case object Moo extends Foo
case object Noo extends Foo
val values = Enum.values[Foo]
println(values)
println(Enum.values[Foo])
println({
val v = Enum.values[Foo]
v
})
}
控制台应打印出三个相同的输出,但是:
List()
List(Moo, Noo)
List(Moo, Noo)
Wny会发生这种情况吗?你怎么处理这个?
如果您能在SBT中测试它,我将不胜感激:https://github.com/ryo0ka/enum
谢谢!
答案 0 :(得分:2)
这确实是一种奇怪的行为,但Scalac很难在这里做得很好:问题是你使用的是嵌套对象。请考虑以下事项:
object EnumTest extends App {
sealed trait Foo
case object Moo extends Foo
case object Noo extends Foo
println(Enum.values[Foo])
case object Bar extends Foo
}
这会打印List(Moo, Noo)
。如果我们在Bar
语句之前移动println
,则会按照我们的预期打印List(Moo, Noo, Bar)
。
如果我们将密封的层次结构移出对象,一切正常。
因此,在您定义案例类的位置显示宏可能不是一个明智的想法。您可以将values
本身设为宏(其实现只需使用右Impl.values
调用WeakTypeTag
。这可能会奏效。