这个问题有点复杂,所以我正在使用一些序言来构建它,以便可以理解问题背后的驱动程序以及演示它的代码示例。
不可变构建器模式
我非常喜欢traits,但是他们可以解决不可变构建器模式的问题。例如,当我创建一个继承构建器特征的对象时,当我在其上调用'set'方法时,构建器特征应该返回原始的相同克隆,除了我刚设置的值。
为了解决这个问题,我经常使用类型语句
trait Builder {
type RealBuilder <: Builder
...
这不是很好用,但它适用于大多数用途。
宇宙模式
我正在尝试从Scala编译器中获取的想法。我有大约十个泛型,但我不希望用户看到它们中的大多数,并且泛型通常以复杂的方式链接在一起
trait Universe[X] {
type A
type B
type RealBuilder <: MyBuilder
type RealBuiltThing <: MyBuiltThing
trait MyBuilder {
def build:
}
trait MyBuiltThing {
}
}
所以这里的好处是宇宙中定义的所有内容都共享相同的泛型,这些类的代码及其用法不会受到大量泛型的污染。
最后一个问题
我想将一个函数传递给Universe中的构建器并获得一个很好的'toString。所以我正在包装函数(在这个简单的例子中只是X),然后将包装的对象传递给setIt方法
class Wrapper[X](x: X, string: String) {
override def toString() = string
}
object Outer {
def someProperty[X: c.WeakTypeTag](c: Context)(someValue: c.Expr[X]):
c.Expr[Outer#InnerBuilder] = { // <----------- This is the first line referenced below
import c.universe._
val xString = show(someValue.tree)
reify { c.Expr[Outer#InnerBuilder](c.prefix.tree).splice.setIt(new Wrapper[X](someValue.splice, c.literal(xString).splice)) }
}
}
class Outer {
trait InnerBuilder {
type RealInnerBuilder <: InnerBuilder;
type B;
def someProperty[X](someValue: X) = macro Outer.someProperty[X]
def setIt[X](w: Wrapper[X]): RealInnerBuilder = {
println("Setting it to " + w) //shows that the code got here
this.asInstanceOf[RealInnerBuilder] //in practice would return a new instance that held the wrapper
}
}
class InnerBuilder1 extends InnerBuilder {
type RealInnerBuilder = InnerBuilder1
}
}
如果我现在创建一个InnerBuilder1并调用'someProperty',则执行println语句。乌拉!
但它很大但是......我失去了一些类型的安全性。伴随对象返回c.Expr [Outer#InnerBuilder]的对象,它真正想做的是返回'c.prefix'的InnerBuilder类。
我很伤心地说,我不完全了解的[]符号是斯卡拉。所以以下可能只是天真。我已经尝试返回c.Expr [c.prefix.actualType],这是我想要的“想法”,但显然不正确。
有人可以告诉我如何将类型安全带回到这个宏吗?
答案 0 :(得分:4)
我想您可能对Context.PrefixType
和Expr.value
感兴趣。以下是我们的测试套件中的相关示例:https://github.com/scala/scala/blob/d5801b9eee7df49894c05dea430a56190cae2112/test/files/run/macro-def-path-dependent-b/Impls_Macros_1.scala#L19。
此外,实际上没有类型安全性丢失,因为2.10中的宏扩展具有允许最精确类型的有趣属性,而不仅仅是在其签名中指定的内容(例如,查看http://meta.plasm.us/posts/2013/06/19/macro-supported-dsls-for-schema-bindings/)。在2.11中,这将改变一点(http://docs.scala-lang.org/overviews/macros/blackbox-whitebox.html),但这种改进返回类型的能力仍将保留。
最后,要知道在2.11中不再需要使用Expr
和reify
,可能会感到宽慰。随着2.11附带的quasiquotes(也可以通过宏天堂编译器插件在2.10中提供),您可以将树几乎以您想要的任何方式放在一起,而无需使类型对齐。使用编写宏impl的更新规则,宏impl可以获取并返回c.Tree
,因此您不需要考虑在[]
的{{1}}中放置什么内容