72

我将从一个例子开始。List.fill这是Scala 2.10 中的 for tuples 作为宏的等价物:

import scala.language.experimental.macros
import scala.reflect.macros.Context

object TupleExample {
  def fill[A](arity: Int)(a: A): Product = macro fill_impl[A]

  def fill_impl[A](c: Context)(arity: c.Expr[Int])(a: c.Expr[A]) = {
    import c.universe._

    arity.tree match {
      case Literal(Constant(n: Int)) if n < 23 => c.Expr(
        Apply(
          Select(Ident("Tuple" + n.toString), "apply"),
          List.fill(n)(a.tree)
        )
      )
      case _ => c.abort(
        c.enclosingPosition,
        "Desired arity must be a compile-time constant less than 23!"
      )
    }
  }
}

我们可以按如下方式使用此方法:

scala> TupleExample.fill(3)("hello")
res0: (String, String, String) = (hello,hello,hello)

这家伙在几个方面是一只奇怪的鸟。首先,arity参数必须是文字整数,因为我们需要在编译时使用它。在以前的 Scala 版本中,方法(据我所知)甚至无法判断其参数之一是否为编译时文字。

其次,Product返回类型是一个谎言——静态返回类型将包括由参数确定的特定数量和元素类型,如上所示。

那么我将如何记录这件事呢?在这一点上,我并不期待 Scaladoc 支持,但我想了解一些约定或最佳实践(不仅仅是确保编译时错误消息清晰),这将使运行到宏方法中——使用它的潜在的奇怪需求——对于 Scala 2.10 库的用户来说并不奇怪。

新宏系统最成熟的演示(例如,ScalaMockSlick以及此处列出的其他)在方法级别上仍然相对没有文档。任何示例或指针都将受到赞赏,包括来自具有类似宏系统的其他语言的示例或指针。

4

1 回答 1

1

我认为记录这些的最好方法是使用示例代码,正如 Miles 在他的基于实验性宏的 shapeless 分支中所做的那样。

于 2013-02-04T12:41:19.093 回答