問題は、渡したコードreify
が基本的にマクロが展開されているポイントに逐語的に配置され、fieldMemberType
そこに何も意味がないことです。
場合によってsplice
は、マクロ展開時に使用している式を、具体化するコードに忍び込ませるために使用できます。たとえば、このトレイトのインスタンスを作成しようとした場合、次のようになります。
trait Foo { def i: Int }
そして、マクロ拡張時にこの変数がありました:
val myInt = 10
次のように書くことができます。
reify { new Foo { def i = c.literal(myInt).splice } }
これはここでは機能しません。つまり、ちょっとしたことを忘れreify
て、ASTを手で書き出す必要があります。残念ながら、これは頻繁に発生します。私の標準的なアプローチは、新しいREPLを開始し、次のように入力することです。
import scala.reflect.runtime.universe._
trait TypeBuilder { type fieldType }
showRaw(reify(new TypeBuilder { type fieldType = String }))
これにより、ASTの数行が吐き出され、開始点としてマクロ定義に切り取って貼り付けることができます。次に、それをいじって、次のようなものを置き換えます。
Ident(TypeBuilder)
これとともに:
Ident(newTypeName("TypeBuilder"))
そしてFINAL
、Flag.FINAL
などで。ASTタイプのメソッドが、それらをビルドするために必要なコードにより正確に対応していることを望みtoString
ますが、何を変更する必要があるかをすぐに理解できます。最終的には次のようになります。
c.Expr(
Block(
ClassDef(
Modifiers(Flag.FINAL),
anon,
Nil,
Template(
Ident(newTypeName("TypeBuilder")) :: Nil,
emptyValDef,
List(
constructor(c),
TypeDef(
Modifiers(),
newTypeName("fieldType"),
Nil,
TypeTree(fieldMemberType)
)
)
)
),
Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
)
)
anon
匿名クラス用に事前に作成した型名はどこにありますか。これconstructor
は、この種のものを少し恐ろしくないものにするために使用する便利なメソッドです(この完全な作業例の最後にその定義があります)。
この式を次のようにまとめると、次のように記述できます。
scala> TypeMemberExample.builderWithType[String]
res0: TypeBuilder{type fieldType = String} = $1$$1@fb3f1f3
だからそれは動作します。(のtypeパラメーターc.universe.Type
からここで取得しますが、古いものとまったく同じように機能します)を取得し、それを使用して特性のタイプメンバーを定義しました。WeakTypeTag
builderWithType
Type
TypeBuilder