【问题标题】:How can I consume a Scala macro/quasiquote for code templates?如何使用代码模板的 Scala 宏/准引用?
【发布时间】: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
}

【问题讨论】:

标签: scala scala-macros scala-quasiquotes


【解决方案1】:

我不认为,您可以使用宏生成新标识符,而不是公开使用它们。

相反,尝试将object ${termName(taName)} extends TypeAdapter 简单地替换为new TypeAdapter 并将宏的调用分配给一个val(如您的第二个示例中所示)。然后,您将引用存储在 val 中的匿名(和生成)类。参数taName 变得多余。

【讨论】:

    猜你喜欢
    • 2021-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-26
    • 1970-01-01
    • 2018-11-16
    • 1970-01-01
    相关资源
    最近更新 更多