【问题标题】:How to use scala macros to create a function object (to create a Map[String, (T) => T])如何使用 scala 宏创建函数对象(创建 Map[String, (T) => T])
【发布时间】:2013-12-19 19:13:53
【问题描述】:

我正在尝试使用 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 建议使用准引号 - 这是我目前使用 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]

【问题讨论】:

  • 我想知道是否有更简单的方法可以在不使用宏的情况下完成您想要完成的工作。您是否试图从本质上获取JsValue 并将其附加到Clazz 的现有实例?即(概念上):Json.obj("id" -> newId) ++ instance 应该等于 instance.copy(id = newId)
  • @ggreiner 是的,当前代码从数据库中检索 Clazz 实例(MySql 之上的 Slick),将其分解为 json,更新 json,将其转换回更新后的 Clazz实例,并使用新实例更新数据库。我们探索宏作为创建Map[String, CopyFun] 的一种方式的原因是我们希望将 json 处理与数据库层分离
  • 你考虑过使用准引号吗?
  • @EugeneBurmako - 感谢您的建议,我已经更新了我的问题 - 假设我已经正确编写了宏,我无法确定调用它的语法

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


【解决方案1】:

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

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

你可能会问为什么quasiquotes不能在他们写的地方弄清楚T是指宏的类型参数然后自动拼接它。实际上这是一个非常合理的问题。在像 Racket 和 Scheme 这样的语言中,准引号足够聪明,可以记住有关它们所使用的词汇上下文的信息,但在 Scala 中,这有点困难,因为该语言中有很多不同的范围。然而,有一个实现目标的计划,并且已经在这个方向上进行研究:https://groups.google.com/forum/#!topic/scala-language/7h27npd1DKI

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-07-01
    • 2019-03-29
    • 1970-01-01
    • 1970-01-01
    • 2018-02-21
    • 2019-09-15
    • 1970-01-01
    • 2019-07-11
    相关资源
    最近更新 更多