是否可以使用宏来简化 Scala 方法参数声明?

Posted

技术标签:

【中文标题】是否可以使用宏来简化 Scala 方法参数声明?【英文标题】:Is it possible to simplify Scala method arguments declaration using macros? 【发布时间】:2013-01-30 14:07:01 【问题描述】:

方法通常用明显的参数名称声明,例如

def myMethod(s: String, image: BufferedImage, mesh: Mesh)  ... 

参数名称对应参数类型。

1) "s" 常用于字符串

2) "i" 表示 Int

3) 一个单词命名类的小写类名(Mesh -> mesh)

4) 长类名的类名最后一个单词小写(BufferedImage -> image)

(当然,所有的方法和参数都不方便。当然,有人会更喜欢其他规则……)

Scala 宏旨在在代码中生成一些表达式。我想编写一些特定的宏来转换为正确的 Scala 表达式,如下所示:

// "arguments interpolation" style
// like string interpolation
def myMethod s[String, BufferedImage, Mesh] 
 /* code using vars "s", "image", "mesh" */ 

// or even better:
mydef myMethod[String, BufferedImage, Mesh] 
 /* code using vars "s", "image", "mesh" */ 

有可能吗?

【问题讨论】:

你到底为什么要这样做?这只会创建你自己的 scala 方言,伴随着所有的混乱,没有任何可口的好处。 似乎每个宏都创建了新的 Scala 方言……我只想写更少的代码,没有任何重复。参数名称看起来像一堆重复的微代码。而且,如果可以创建这样的宏,我们可以对其进行试验、改进,甚至尝试使其普遍流行。 是的,这是真的,在某种程度上,每个宏都会创建一些方言,但他们这样做是为了启用在普通 scala 中不容易实现的东西(至少在没有大量样板的情况下不是这样)有效率的)。或者换句话说,它们带来了一些新的和有价值的东西,这应该在很大程度上弥补了离开普通 scala 的缺点。不仅你的提议没有带来太多,而且非常模糊(“s”代表字符串?你会在哪里结束?你需要一个巨大的备忘单来知道你所有参数的类型/名称吗?)。只是我的 2 美分。 我不希望自己的 Scala 完全没有参数名称。我只想要一些宏来避免参数名称完全明显的重复。给定的“规则”只是一个例子。现在我只是无法尝试让它变得更好。但是每个人都可以检查自己的代码,并看到许多带有这种重复的小而简单的方法。 【参考方案1】:

目前这是不可能的,而且可能永远不会。宏不能引入自己的语法——它们必须通过有效的 Scala 代码(可以在编译时执行)来表示,而且它们还必须生成有效的 Scala 代码(最好是有效的 Scala AST)。

您显示的两个示例都不是有效的 Scala 代码,因此宏无法处理它们。尽管如此,Macro Paradise 的当前夜间版本包括无类型宏。它们允许编写扩展后进行类型检查的 Scala 代码,这意味着可以编写:

forM(i = 0; i < 10; i += 1) 
  println(i)

请注意,第一个参数列表中的花括号是必需的,因为尽管编写代码时未对代码进行类型检查,但它必须表示有效的 Scala AST。

这个宏的实现如下所示:

def forM(header: _)(body: _) = macro __forM

def __forM(c: Context)(header: c.Tree)(body: c.Tree): c.Tree = 
  import c.universe._
  header match 
    case Block(
      List(
        Assign(Ident(TermName(name)), Literal(Constant(start))),
        Apply(Select(Ident(TermName(name2)), TermName(comparison)), List(Literal(Constant(end))))
      ),
      Apply(Select(Ident(TermName(name3)), TermName(incrementation)), List(Literal(Constant(inc))))
    ) =>

    // here one can generate the behavior of the loop
    // but omit full implementation for clarity now ...

  

宏只需要一个树,而不是已经过类型检查的表达式,它在扩展之后进行类型检查。方法调用本身需要两个参数列表,如果使用下划线,则其参数类型可以在扩展阶段之后延迟。

目前有一点点documentation 可用,但由于它是非常测试版,很多事情将来可能会改变。

使用类型宏可以这样写:

object O extends M 
  // traverse the body of O to find what you want


type M(x: _) = macro __M

def __M(c: Context)(x: c.Tree): c.Tree = 
  // omit full implementation for clarity ...

这很好,可以延迟整个身体的类型检查,因为它可以冷却东西......


目前还没有计划可以更改 Scalas 语法的宏,并且可能不是一个好主意。我不能说它们是否会在某一天发生,只有未来才能告诉我们这一点。

【讨论】:

【参考方案2】:

除了“为什么”(不是真的,你为什么要这样做?)之外,答案是否定的,因为据我所知,宏不能(在当前状态下)生成方法或类型,只能生成表达式。

【讨论】:

更不用说改变 def x s[...] 无法解析的解析方式了。 pedrofurla,我不知道确切的可能方式,我只是想避免重复。它可能看起来像def myMethod(string: _, image: _, mesh: _)。你确定没有办法吗? 不,没有。最接近的可能是类型宏 (docs.scala-lang.org/overviews/macros/typemacros.html),它可以获取类或对象的主体并将其重写为其他内容,但类型宏很可能不会在 2.10.x 中发布,并且可能甚至不包含在 2.11 中。

以上是关于是否可以使用宏来简化 Scala 方法参数声明?的主要内容,如果未能解决你的问题,请参考以下文章

可变参数编程

没有参数的 Scala 方法

Scala 变量

C语言,将数字和字符串拼成一个字符串。用宏来做,用api谁都会!

Scala柯里化及其应用实践

Scala隐式参数