如何使用Scala宏创建函数对象(以创建Map [String,(T)=> T]) [英] How to use scala macros to create a function object (to create a Map[String, (T) => T])

查看:90
本文介绍了如何使用Scala宏创建函数对象(以创建Map [String,(T)=> T])的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to use Scala macros to create a case class map of single-parameter copy methods, with each method accepting a Play Json JsValue and a case class instance, and returning an updated copy of the instance. However, I am running into problems with the macro syntax for returning a function object.

给出案例类

case class Clazz(id: Int, str: String, strOpt: Option[String])

目的是创建类的复制方法的映射

the intention is to create a map of the class's copy methods

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), ...)

我发现了两个相关的问题:

I have found two related questions:

使用宏创建案例类字段图:

Using macros to create a case class field map: Scala Macros: Making a Map out of fields of a class in Scala

使用宏访问案例类复制方法:如何使用Scala宏在方法调用中对命名参数进行建模?

Accessing the case class copy method using a macro: Howto model named parameters in method invocations with Scala macros?

...但是我在如何创建函数对象方面陷入困境,以便可以返回Map[String, (JsValue, T) => T]

...but I am stuck at how I can create a function object so that I can return a Map[String, (JsValue, T) => T]

感谢Eugene Burmako建议使用准引用-这是我目前使用Scala 2.11.0-M7的地方,我的代码基于

Thanks to Eugene Burmako's suggestion to use quasiquotes - this is where I'm currently at using Scala 2.11.0-M7, basing my code on Jonathan Chow's post (I switched from using (T, JsValue) => T to (T, String) => T to simplify my REPL imports)

Edit2:现在加入了$ tpe拼接

Now incorporating $tpe splicing

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]

推荐答案

您的代码中几乎所有内容都正确无误,除了您需要将T拼接为准引号(即写$tpe而不是仅仅T.

You got almost everything right in your code, except for the fact that you need to splice T into a quasiquote, i.e. to write $tpe instead of just T.

为了使它看起来更自然,我通常在宏中显式声明类型标签证据,例如def foo[T](c: Context)(implicit T: c.WeakTypeTag[T]) = ....在那之后,我只写了$T,看起来就不错了:)

For that to look more natural, I usually explicitly declare type tag evidences in macros, e.g. def foo[T](c: Context)(implicit T: c.WeakTypeTag[T]) = .... After that I just write $T, and it looks almost fine :)

您可能会问,为什么准引用不能仅仅在写它们的地方弄清楚呢?T引用宏的类型参数然后自动将其拼接起来.实际上,这是一个非常合理的问题.在诸如Racket和Scheme这样的语言中,准语符足够聪明,可以记住有关其所编写的词法上下文的信息,但是在Scala中,这有点困难,因为该语言存在许多不同的作用域.但是,有一个计划可以实现,并且已经在朝着这个方向进行研究: https://groups.google.com/forum/#!topic/scala-language/7h27npd1DKI .

You might ask why quasiquotes can't just figure out that in the place where they're written T refers to the type parameter of a macro and then automatically splice it in. That would be very reasonable question, actually. In languages like Racket and Scheme, quasiquotes are smart enough to remember things about the lexical context they are written in, but in Scala this is a bit more difficult, because there are so many different scopes in the language. Yet, there's a plan to get there, and research in that direction in already underway: https://groups.google.com/forum/#!topic/scala-language/7h27npd1DKI.

这篇关于如何使用Scala宏创建函数对象(以创建Map [String,(T)=> T])的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆