Проблема в том, что код, который вы передаете 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
, и так далее. Я бы хотел, чтобы методы toString
для типов AST более точно соответствовали коду, необходимому для их построения, но вы довольно быстро поймете, что вам нужно изменить. Вы получите что-то вроде этого:
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
Так что это работает. Мы взяли c.universe.Type
(которое я получил здесь из WeakTypeTag
параметра типа в builderWithType
, но он будет работать точно так же с любым старым Type
) и использовали его для определения члена типа нашего признака TypeBuilder
.
person
Travis Brown
schedule
10.12.2012