是否有可能编写一个scala宏,其返回类型取决于参数?

时间:2012-08-18 11:21:44

标签: macros scala-2.10 scala-macros

对于DSL,我希望能够做到这样的事情:

object Creator { 
   def create[T](s :String) :Foo[T] = macro createImpl[T]
   def createImpl[T](c :Context)(s :c.Expr[String]) : c.Expr[Foo[T]] = {
        reify(new Foo[Any]())
   }
 }

我的问题是将reify中的Any替换为将返回正确参数化版本的内容。

(上面我使用字符串参数,但在最终版本中,我打算使用T类的伴随对象作为标记来了解Function1 [T,Unit]的参数类型)

1 个答案:

答案 0 :(得分:2)

你需要: a)写new Foo[T]()而不是new Foo[Any]()(简单) b)通过使用上下文绑定声明参数T来向宏传递类型AbsTypeTag[T]的表示,即类型T的值:[T: c.AbsTypeTag]

这是我在Scala 2.10.0-M7中测试过的代码。 修改即可。在2.10.0-RC1 AbsTypeTag已重命名为WeakTypeTag。关于宏和类型标签的其他所有内容都保持不变。

Creator.scala:

import language.experimental.macros
import reflect.macros.Context
class Foo[T]
object Creator {
  def create[T](s: String): Foo[T] = macro createImpl[T]
  def createImpl[T: c.AbsTypeTag](c: Context)(s: c.Expr[String]): c.Expr[Foo[T]] = {
    import c.universe._
    reify(new Foo[T]())
  }
}

MacroClient.scala:

object Main extends App {
  println (Creator.create[Int](""))
}

请注意,如果省略type参数,您将收到一个奇怪的错误:

scala> Creator.create[Int]("")
res2: Foo[Int] = Foo@4888884e

scala> Creator.create("")
<console>:12: error: macro has not been expanded
              Creator.create("")
                      ^

你也写道:

  

(上面我使用字符串参数,但在我计划使用的最终版本中   T类的伴随对象作为知道参数类型的标记   一个Function1 [T,Unit])

但如果我做对了,这听起来像个坏主意。调用语法不是写Creator.create[T](otherArgs),而是Creator.create(T, otherArgs),而不是一个很大的优点(如果有的话)。但是你甚至无法获得后一种语法:如果class Aobject A是随播广告,则其类型不相关:第一种类型为A,第二种类型为A.type } A是伴随对象,而不是类A的类型。


更新:如果您可以控制Creator create Foo,如何使Foo语法生效并返回Foo的实例。 由于您询问Any的{​​{1}}类型参数,我假设您询问的是类型参数。只有当您希望reify静态返回类型为Creator.create而不是T时才有意义;否则,你应该澄清你的问题。

这里的问题与宏没什么关系。 Any将对象Creator create Foo传递给Foo,其声明需要通过类型表达式给出Creator.create类型Foo.type。 Scala中的类型表达式非常有限 - 例如,它们不能使用反射。但是给定一个类型,他们可以选择其类型成员。

Foo

这是有限的,因为你需要改变伴侣对象,但我很确定你不能做得更好。我不知道,在类型表达式中(在声明返回类型trait Companion[Class] //How to declare a companion class Foo object Foo extends Companion[Foo] /*I'm cheating: an implementation of Companion does not need to be a true Companion. You can add documentation to explain how Companion is supposed to be used. */ object Bar extends Companion[Foo] //But this is also useful - you can't create your own companion objects for pre-existing types, but you can still create the right instances of Companion: object pInt extends Companion[Int] object Creator { //T with Companion[S] is needed to workaround a type inference bug (SI-5298) and allow S to be correctly inferred. def create[S, T <: Companion[S]](obj: T with Companion[S]): S = ??? } 时,可以用来代替S)从同伴对象获取到其关联对象类型一般,我认为没有。

现在,将上面的内容更改为使用宏很简单:

create