実行時に AST からクラスをコンパイルおよび構築するために、scala 2.10 のツールボックス API を使用しています。将来のクラス定義にコンパイルしてミックスしたい特性の AST がありますが、クラスに適用するためのハンドルを取得できません。
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> import scala.tools.reflect.ToolBox
import scala.tools.reflect.ToolBox
scala> val tb = scala.reflect.runtime.currentMirror.mkToolBox()
tb: scala.tools.reflect.ToolBox[reflect.runtime.universe.type] = scala.tools.reflect.ToolBoxFactory$ToolBoxImpl@7469d79a
scala> val ast_trait = tb.parse("trait T")//My AST is manually constructed starting with ClassDef
ast_trait: tb.u.Tree = abstract trait T extends scala.AnyRef
//compile and execute the tree
scala> tb.eval(ast_trait)
res0: Any = ()
//attempt to define a class that
scala> tb.eval(tb.parse("class C extends T"))implements T. In my code this is a constructed AST
scala.tools.reflect.ToolBoxError: reflective compilation has failed:
not found: type T
/*stack trace*/
クラス/特性がツールボックスでコンパイルされると、ラップされて __wrapper$[wrapCount]$[uuid]$[name] のような名前が付けられるため、これは失敗します。
問題は、クラス定義で使用される定義済みの特性へのハンドルをどのように取得するかということです。
明らかな解決策は、 をscala.reflect.runtime.universe.Type
介してトレイトのインスタンスを取得することでしたtypeOf[T]
。
//Obtain an AST via tb.parse
scala> val ast_type = tb.parse("scala.reflect.runtime.universe.typeOf[T]")
ast_type: tb.u.Tree = scala.reflect.runtime.universe.typeOf[T]
//eval the trait defintion and typeOf in the same block
scala> tb.eval(Block(ast_trait, ast_type))
warning: there were 1 deprecation warning(s); re-run with -deprecation for details
scala.tools.reflect.ToolBoxError: reflective compilation has failed:
No TypeTag available for T
not enough arguments for method typeOf: (implicit ttag: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.Type.
Unspecified value parameter ttag.
/*stack trace*/
これが失敗する理由は完全にはわかりませんが、暗黙の型タグとツールボックスのコンパイルをどのような形式で使用してもうまくいきませんでした。
//A simpler all in one example
scala> tb.eval(tb.parse("trait T; scala.reflect.runtime.universe.typeOf[T]"))
scala.tools.reflect.ToolBoxError: reflective compilation has failed:
No TypeTag available for T
not enough arguments for method typeOf: (implicit ttag: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.Type.
Unspecified value parameter ttag.
/*stack trace*/
ラッピングを無効にして、特性が AST で指定された名前で定義されるようにする方法はありますか?
そうでない場合、特性の fullName を取得するにはどうすればよいですか?
更新:classOf
特性が実際にクラスに適用されるまで、使用の提案は魔法のように機能しました。
scala> val mirror = scala.reflect.runtime.currentMirror
scala> val clazz = tb.eval(tb.parse("trait T{def m = 0}; classOf[T]")).asInstanceOf[Class[_]]
clazz: Class[_] = interface __wrapper$5$36e5027b48a44570a47be32d536aa67d.__wrapper$5$36e5027b48a44570a47be32d536aa67d$T$1
scala> val tpe = mirror.classSymbol(clazz)
tpe: reflect.runtime.universe.ClassSymbol = trait T$1
scala> val tree = ClassDef(Modifiers(), "C", List(), Template(
| List(Ident(tpe)),
| emptyValDef,
| List()))
tree: reflect.runtime.universe.ClassDef = class C extends T$1
scala> tb.eval(tree)
scala.tools.reflect.ToolBoxError: reflective compilation has failed:
class C needs to be abstract, since method m in trait T$1 of type ()Int is not defined
/*stack trace*/
トレイトの実装が失われ、単なるインターフェースとして適用されているようです (これは、classOf によって返される Java クラスが定義するものです)。
私は、ツールボックスのラッパーが特性の実装のために Java クラスの名前を変更し (通常は同じクラス名の後に「$」が続く)、新しい uuid を使用して、コンパイラがそれを見つけることができず、それが単なるインターフェイスであると想定していると仮定します。
実装のある特性をクラスに適用するにはどうすればよいですか?