宏奇怪的动作(想要一个更好的标题)

时间:2014-12-23 03:43:20

标签: scala macros

我制作了一个枚举宏,最终出现了一种奇怪的行为。

这是主要的实施方式:

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

谢谢!

1 个答案:

答案 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。这可能会奏效。