问题描述
对于DSL,我希望能够执行以下操作:
For a DSL I would like to be able to do something like:
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
替换为可以返回正确参数化版本的东西.
My problem is to replace the Any
in reify with something that will return the correctly parametrized version.
(上面我使用了字符串参数,但是在最终版本中,我计划使用T类的伴随对象作为标记来了解Function1 [T,Unit]的参数类型)
(above I use a string argument, but in the final version I plan to use the companion object of class T as a marker to know the argument-type of a Function1[T,Unit])
推荐答案
您需要:a)写new Foo[T]()
而不是new Foo[Any]()
(简单)b)通过使用上下文绑定:[T: c.AbsTypeTag]
声明参数T
,在宏中传递类型T
的表示形式,即类型AbsTypeTag[T]
的值.
You need to:a) write new Foo[T]()
instead of new Foo[Any]()
(easy)b) pass in the macro a representation of type T
, namely, a value of type AbsTypeTag[T]
, by declaring the parameter T
using a context bound: [T: c.AbsTypeTag]
.
这是我在Scala 2.10.0-M7中测试的代码. 编辑.在2.10.0-RC1中,AbsTypeTag
已重命名为WeakTypeTag
.关于宏和类型标签的其他所有内容都保持不变.
Here's code which I tested in Scala 2.10.0-M7. Edit. In 2.10.0-RC1 AbsTypeTag
has been renamed to WeakTypeTag
. Everything else about macros and type tags remains the same.
Creator.scala:
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:
MacroClient.scala:
object Main extends App {
println (Creator.create[Int](""))
}
请注意,如果省略type参数,则会出现奇怪的错误:
Note that if you omit the type parameter, you will get a weird error:
scala> Creator.create[Int]("")
res2: Foo[Int] = Foo@4888884e
scala> Creator.create("")
<console>:12: error: macro has not been expanded
Creator.create("")
^
您还写:
但是,如果我做对了,这听起来是个坏主意.而不是编写Creator.create[T](otherArgs)
,调用语法将类似于Creator.create(T, otherArgs)
,并不是很大的优势(如果有的话).但您甚至无法获得后一种语法:如果class A
和object A
是同伴,则它们的类型不相关:第一个具有类型A
,第二个具有类型A.type
,其中A
是同伴对象,而不是类A
的类型.
but if I get it right, this sounds like a bad idea. Instead of writing Creator.create[T](otherArgs)
, the call syntax would be something like Creator.create(T, otherArgs)
, not a big advantage (if any). But you can't even get the latter syntax: if class A
and object A
are companions, their types are not related: the first has type A
, the second has type A.type
where A
is the companion object and not the type of class A
.
更新:如果可以控制Foo
,如何使Creator create Foo
语法起作用并返回Foo
的实例.因为您询问的是reify
的Any
类型参数,所以我假设您是在询问类型参数.仅当您希望Creator.create
的 static 返回类型为T
而不是Any
时才有意义;否则,您应该澄清您的问题.
Update: how to get the Creator create Foo
syntax to work and return an instance of Foo
, if you have control over Foo
.Since you ask about the Any
type argument to reify
, I assume you are asking about the type argument. That makes only sense if you want the static return type of Creator.create
to be T
and not Any
; otherwise, you should clarify your question.
这里的问题与宏关系不大. Creator create Foo
将对象Foo
传递给Creator.create
,对于给定的Foo.type
,该对象的声明需要通过类型表达式来表示类型Foo
. Scala中的类型表达式非常有限-例如,它们不能使用反射.但是给定类型后,他们可以选择其类型成员.
The problem here has little to do with macros. Creator create Foo
passes the object Foo
to Creator.create
, whose declaration needs to express, given Foo.type
, the type Foo
through a type expression. Type expressions in Scala are quite limited - they can't use reflection, for instance. But given a type, they can select its type members.
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 = ???
}
这是有限的,因为您需要更改伴随对象,但是我敢肯定您做得更好.我不知道在类型表达式中(在声明create
的返回类型时可以使用什么代替S
)从同伴对象到其关联的类类型的方法,但我认为没有.
This is limited since you need to alter the companion object, but I'm pretty sure you can't do better. I know no way, in a type expression (what you can use in place of S
when declaring the return type of create
) of getting from a companion object to its associated class type in general, and I don't think there's one.
现在,更改以上内容以使用宏非常简单:
Now, changing the above to use macros is straightforward:
import language.experimental.macros
import reflect.macros.Context
class Foo[T]
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]): Foo[S] = macro createImpl[S, T]
def createImpl[S: c.AbsTypeTag, T <: Companion[S]: c.AbsTypeTag](c: Context)(obj: c.Expr[T with Companion[S]]): c.Expr[Foo[S]] = {
import c.universe._
reify(new Foo[S]())
}
}
这篇关于是否可以编写一个scala宏,其返回类型取决于参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!