为什么编译器不考虑对象?

时间:2014-12-22 11:09:50

标签: scala macros enums

我试着收集一个密封的班级孩子:

import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros

object Enum {
  def values[A]: Seq[A] = macro EnumImpl.values[A]

  class EnumImpl(val c: Context) {
    import c.universe._

    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 values[A: c.WeakTypeTag]: c.Expr[A] = {
      val enumSymbol = weakTypeOf[A].typeSymbol.asSealedClass
      val elemSymbols = enumSymbol.knownDirectSubclasses.toList
      val elemIdents = elemSymbols.map(s => Ident(s.asCaseClass))
      val elemSeqSymbol = weakTypeOf[Seq[A]].typeSymbol
      c.Expr(Apply(Ident(elemSeqSymbol.companion), elemIdents))
    }
  }
}

我用它测试了它:

sealed trait Foo
case object Moo extends Foo

println(Enum.values[Foo])

然后编译器声称:

object Moo is not a value

所以我尝试了它的同伴:

...
val elemIdents = elemSymbols.map(s => Ident(s.asCaseClass.companion))
...

然后编译器声称:

not found: value <none>

因此对象Moo不是对象XD

我做错了什么?

1 个答案:

答案 0 :(得分:5)

compiler code中的这个图描述了类模块和模块类的一般情况(内部对象称为模块):

The internal representation of classes and objects:

class Foo is "the class" or sometimes "the plain class"
object Foo is "the module"
class Foo$ is "the module class" (invisible to the user: it implements object Foo)

class Foo  <
^   ^ (2)   \
|   |  |     \
|  (5) |     (3)
|   |  |       \
(1) v  v        \
object Foo (4)-> > class Foo$

(1) companionClass
(2) companionModule
(3) linkedClassOfClass
(4) moduleClass
(5) companionSymbol

这些东西都是内部的。但是,重要的是要看到,当您查询特征的子类时,您将获得模块类而不是模块本身。

您可以通过调用模块类符号上的module从模块类到模块:

val elemIdents = for {
  elemSym <- elemSymbols
  if elemSym.isModuleClass // TODO fail if not a module class? Ignore?
} yield Ident(elemSym.asClass.module)