我想用Scala宏創建一個單參數copy
方法的case class map,每種方法接受一個Play Json JsValue
和一個case類實例,並返回該實例的更新副本。但是,我遇到了返回函數對象的宏語法問題。如何使用Scala宏創建一個函數對象(創建一個Map [String,(T)=> T])
給定的情況下,類
case class Clazz(id: Int, str: String, strOpt: Option[String])
的目的是建立地圖類的複製方法的
implicit def jsonToInt(json: JsValue) = json.as[Int]
implicit def jsonToStr(json: JsValue) = json.as[String]
implicit def jsonToStrOpt(json: JsValue) = json.asOpt[String]
Map("id" -> (json: JsValue, clazz: Clazz) = clazz.copy(id = json),
"str" -> (json: JsValue, clazz: Clazz) = clazz.copy(str = json), ...)
我已經發現了兩個相關的問題:
使用宏創建案例類實地圖:Scala Macros: Making a Map out of fields of a class in Scala
訪問cas使用電子類複製方法的宏:Howto model named parameters in method invocations with Scala macros?
...但我停留在如何創建一個函數對象,這樣我可以返回Map[String, (JsValue, T) => T]
編輯:感謝尤金Burmako的建議,使用quasiquotes - 這是我目前正在使用Scala 2.11.0-M7,基於我的代碼Jonathan Chow's post(我從使用(T,JsValue)=> T切換到(T,String)=> T來簡化我的REPL進口)
編輯2:現在結合$ tpe拼接
import scala.language.experimental.macros
implicit def strToInt(str: String) = str.toInt
def copyMapImpl[T: c.WeakTypeTag](c: scala.reflect.macros.Context):
c.Expr[Map[String, (T, String) => T]] = {
import c.universe._
val tpe = weakTypeOf[T]
val fields = tpe.declarations.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor => m
}.get.paramss.head
val methods = fields.map { field => {
val name = field.name
val decoded = name.decoded
q"{$decoded -> {(t: $tpe, str: String) => t.copy($name = str)}}"
}}
c.Expr[Map[Sring, (T, String) => T]] {
q"Map(..$methods)"
}
}
def copyMap[T]: Map[String, (T, String) => T] = macro copyMapImpl[T]
case class Clazz(i: Int, s: String)
copyMap[Clazz]
我想知道是否有一種更簡單的方法可以在不使用宏的情況下完成所要完成的任務。你是否試圖採用'JsValue'並將其附加到'Clazz'的現有實例?即(概念上):'Json.obj(「id」 - > newId)++實例'應該等於'instance.copy(id = newId)' – ggreiner
@ggreiner是的,當前代碼從數據庫中檢索一個'Clazz'實例(在MySql之上的Slick),將它分解爲json,更新json,將其轉換回更新的'Clazz'實例,並用新實例更新數據庫。我們正在探索宏作爲創建'Map [String,CopyFun]'的一種方式的原因是我們想要將json處理與數據庫層分離 –
您是否考慮過使用quasiquotes? –