如何使用scala宏创建一个函数对象(创建一个Map [String,(T)=> T])

时间:2013-12-19 19:13:53

标签: scala reflection macros case-class scala-macros

我正在尝试使用Scala宏来创建单参数copy方法的案例类映射,每个方法都接受Play Json JsValue和案例类实例,并返回更新后的副本实例。但是,我遇到了返回函数对象的宏语法问题。

给出一个案例类

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

使用宏访问案例类复制方法:Howto model named parameters in method invocations with Scala macros?

...但我仍然坚持如何创建一个函数对象,以便我可以返回Map[String, (JsValue, T) => T]


编辑:感谢Eugene Burmako建议使用quasiquotes - 这是我目前正在使用Scala 2.11.0-M7的地方,我的代码基于Jonathan Chow's post(我从使用(T,JsValue)切换= > T to(T,String)=> T以简化我的REPL导入)

Edit2:现在整合$ 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]

1 个答案:

答案 0 :(得分:4)

你的代码中几乎所有内容都是正确的,除了你需要将T拼接成一个quasiquote,即写$tpe而不是T

为了看起来更自然,我通常在宏中明确声明类型标记证据,例如def foo[T](c: Context)(implicit T: c.WeakTypeTag[T]) = ...。之后我只写$T,看起来几乎没问题:)

你可能会问为什么quququotes不能只知道在它们被写入的地方T指的是宏的类型参数然后自动将其拼接。这将是非常合理的问题,其实。在诸如Racket和Scheme之类的语言中,quasiquotes足够聪明,可以记住它们所写的词汇上下文,但在Scala中这有点困难,因为语言中有很多不同的范围。然而,有一个计划到那里,并且正在进行这方面的研究:https://groups.google.com/forum/#!topic/scala-language/7h27npd1DKI