【发布时间】:2014-01-09 11:51:48
【问题描述】:
这个问题有点复杂,所以我用一些序言来补充它,以便可以理解问题背后的驱动程序以及演示它的代码示例。
- 具有特征的不可变生成器模式
- 宇宙格局
- 终于 问题!
不可变生成器模式
我非常喜欢 trait,但它们会给不可变构建器模式带来问题。例如,当我创建一个继承 builder trait 的对象时,当我在其上调用“set”方法时,builder trait 应该返回一个与原始对象相同的克隆,除了我刚刚设置的值。
为了解决这个问题,我经常使用类型声明
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 类。
我很遗憾地说我不完全理解 [] 符号是 Scala。所以以下可能只是幼稚的。我试过返回 c.Expr[c.prefix.actualType],这是我想要的“想法”,但显然不正确。
谁能告诉我如何将类型安全带回这个宏?
【问题讨论】:
标签: scala scala-macros