【发布时间】:2019-01-21 07:04:14
【问题描述】:
我想在编译时生成一堆遵循简单模式的对象,所以我写了以下宏:
object MyMacro {
def readWrite[T](taName: String, readParse: String => T, label: String, format: T => String): Any = macro readWriteImpl[T]
def readWriteImpl[T: c.WeakTypeTag](c: Context)(taName: c.Expr[String], readParse: c.Expr[String => T], label: c.Expr[String], format: c.Expr[T => String]): c.Expr[Any] = {
import c.universe._
def termName(s: c.Expr[String]): TermName = s.tree match {
case Literal(Constant(s: String)) => TermName(s)
case _ => c.abort(c.enclosingPosition, "Not a string literal")
}
c.Expr[Any](q"""
object ${termName(taName)} extends TypeAdapter.=:=[${implicitly[c.WeakTypeTag[T]].tpe}] {
def read[WIRE](path: Path, reader: Transceiver[WIRE], isMapKey: Boolean = false): ${implicitly[c.WeakTypeTag[T]].tpe} =
reader.readString(path) match {
case null => null.asInstanceOf[${implicitly[c.WeakTypeTag[T]].tpe}]
case s => Try( $readParse(s) ) match {
case Success(d) => d
case Failure(u) => throw new ReadMalformedError(path, "Failed to parse "+${termName(label)}+" from input '"+s+"'", List.empty[String], u)
}
}
def write[WIRE](t: ${implicitly[c.WeakTypeTag[T]].tpe}, writer: Transceiver[WIRE], out: Builder[Any, WIRE]): Unit =
t match {
case null => writer.writeNull(out)
case _ => writer.writeString($format(t), out)
}
}
""")
}
}
我不确定 readWrite 和 readWriteImpl 的返回值是否正确——编译器强烈抱怨某些断言失败!
我也不确定如何实际使用这个宏。首先我尝试了(在一个单独的编译单元中):
object TimeFactories {
MyMacro.readWrite[Duration](
"DurationTypeAdapterFactory",
(s: String) => Duration.parse(s),
"Duration",
(t: Duration) => t.toString)
}
没用。如果我尝试引用 TimeFactories.DurationTypeAdapterFactory ,则会收到一条错误消息,指出找不到它。接下来我想我会尝试将它分配给一个 val...也没有用:
object Foo {
val duration = MyMacro.readWrite[Duration](
"DurationTypeAdapterFactory",
(s: String) => Duration.parse(s),
"Duration",
(t: Duration) => t.toString).asInstanceOf[TypeAdapterFactory]
}
我怎样才能把它连接起来,这样我才能得到这样编译的生成代码:
object TimeFactories{
object DurationTypeAdapterFactory extends TypeAdapter.=:=[Duration] {
def read[WIRE](path: Path, reader: Transceiver[WIRE], isMapKey: Boolean = false): Duration =
reader.readString(path) match {
case null => null.asInstanceOf[Duration]
case s => Try( Duration.parse(s) ) match {
case Success(d) => d
case Failure(u) => throw new ReadMalformedError(path, "Failed to parse Duration from input 'Duration'", List.empty[String], u)
}
}
def write[WIRE](t: Duration, writer: Transceiver[WIRE], out: Builder[Any, WIRE]): Unit =
t match {
case null => writer.writeNull(out)
case _ => writer.writeString(t.toString, out)
}
}
// ... More invocations of the readWrite macro with other types for T
}
【问题讨论】:
-
也许可以阅读docs.scala-lang.org/overviews/macros/usecases.html 和docs.scala-lang.org/overviews/macros/typeproviders.html 的文档并查看链接示例。那么答案会更有意义。
-
从签名中可以看出,宏不应该用于生成
objects,它们应该用于生成值,可能是新的创建匿名类型。我认为你应该重新考虑你的方法。可能有助于指定您真正的高级问题,以免陷入XY-problem。此外,查看类似 spray-json 或 circe 的 Scala JSON 序列化库可能会有所帮助。
标签: scala scala-macros scala-quasiquotes