sjson 中奇怪的 scala 片段(宏?)
Weird scala snippet in sjson (macro?)
我在查看sjson的源代码时发现了这段奇怪的代码:
<#list 2..22 as i>
<#assign typeParams><#list 1..i as j>T${j}<#if i !=j>,</#if></#list></#assign>
def asProduct${i}[S, ${typeParams}](<#list 1..i as j>f${j}: String<#if i != j>,</#if></#list>)(apply : (${typeParams}) => S)(unapply : S => Product${i}[${typeParams}])(implicit <#list 1..i as j>bin${j}: Format[T${j}]<#if i != j>,</#if></#list>) = new Format[S]{
def writes(s: S) = {
val product = unapply(s)
JsObject(
List(
<#list 1..i as j>
(tojson(f${j}).asInstanceOf[JsString], tojson(product._${j}))<#if i != j>,</#if>
</#list>
))
}
def reads(js: JsValue) = js match {
case JsObject(m) => // m is the Map
apply(
<#list 1..i as j>
fromjson[T${j}](m(JsString(f${j})))<#if i != j>,</#if>
</#list>
)
case _ => throw new RuntimeException("object expected")
}
}
</#list>
乍一看,它看起来像一个宏,但我不确定,因为我看到的是不同的,它们使用 类 修改 Scala AST,如 WeakTypeTag 等等。我知道此方法正在生成具有 20 个不同参数列表的 asProduct 方法。但是,当我在我的 REPL 中复制这段代码时,它没有被正确解析,我得到以下错误:
<console>:1: error: identifier expected but double literal found.
还有一堆错误。
我想知道:
- 为什么以及如何使用此方法
- 学习掌握此内容的更多资源
- 为什么在其他库中他们不使用它,他们更喜欢 copy/paste 手动方法(这实际上是有原因的还是他们不知道它的存在?)
如评论所述,sjson 使用 FMPP 模板生成 scala 文件。您可以在项目构建中找到它是如何完成的:
https://github.com/debasishg/sjson/blob/master/project/SJsonProject.scala#L61-L86
因此它与 scala 宏功能无关。
根据我的经验,大多数项目都需要这种代码生成,为此使用一些库或 SBT 插件。
FMPP 方法的替代方法是使用 SBT 插件。我个人更喜欢 sbt-boilerplate 插件。它易于使用,并且在模板中需要的代码更少。但它仍然是基于模板的代码生成方法。
我在查看sjson的源代码时发现了这段奇怪的代码:
<#list 2..22 as i>
<#assign typeParams><#list 1..i as j>T${j}<#if i !=j>,</#if></#list></#assign>
def asProduct${i}[S, ${typeParams}](<#list 1..i as j>f${j}: String<#if i != j>,</#if></#list>)(apply : (${typeParams}) => S)(unapply : S => Product${i}[${typeParams}])(implicit <#list 1..i as j>bin${j}: Format[T${j}]<#if i != j>,</#if></#list>) = new Format[S]{
def writes(s: S) = {
val product = unapply(s)
JsObject(
List(
<#list 1..i as j>
(tojson(f${j}).asInstanceOf[JsString], tojson(product._${j}))<#if i != j>,</#if>
</#list>
))
}
def reads(js: JsValue) = js match {
case JsObject(m) => // m is the Map
apply(
<#list 1..i as j>
fromjson[T${j}](m(JsString(f${j})))<#if i != j>,</#if>
</#list>
)
case _ => throw new RuntimeException("object expected")
}
}
</#list>
乍一看,它看起来像一个宏,但我不确定,因为我看到的是不同的,它们使用 类 修改 Scala AST,如 WeakTypeTag 等等。我知道此方法正在生成具有 20 个不同参数列表的 asProduct 方法。但是,当我在我的 REPL 中复制这段代码时,它没有被正确解析,我得到以下错误:
<console>:1: error: identifier expected but double literal found.
还有一堆错误。
我想知道:
- 为什么以及如何使用此方法
- 学习掌握此内容的更多资源
- 为什么在其他库中他们不使用它,他们更喜欢 copy/paste 手动方法(这实际上是有原因的还是他们不知道它的存在?)
如评论所述,sjson 使用 FMPP 模板生成 scala 文件。您可以在项目构建中找到它是如何完成的:
https://github.com/debasishg/sjson/blob/master/project/SJsonProject.scala#L61-L86
因此它与 scala 宏功能无关。
根据我的经验,大多数项目都需要这种代码生成,为此使用一些库或 SBT 插件。
FMPP 方法的替代方法是使用 SBT 插件。我个人更喜欢 sbt-boilerplate 插件。它易于使用,并且在模板中需要的代码更少。但它仍然是基于模板的代码生成方法。