对象构造的组合性
The basic problem here is, that, in Scala as well as in all other languages I know, object construction doesn't compose. Consider two abstract operations op1 and op2, where op1 makes property p1 true and where op2 makes property p2 true. These operations are composable with respect to a composition operation ○, if op1 ○ op2 makes both p1 and p2 true. (Simplified, properties also need a composition operation, for example conjunction such as and.)
让我们考虑一下new
操作和财产new A(): A
,即通过调用创建的对象new A
属于类型A
. The new
操作缺乏组合性,因为没有操作/语句/函数f
在 Scala 中,您可以编写new A
and new B
这样f(new A, new B): A with B
。 (简单来说,不用想太多A
and B
必须是类、特征、接口或其他)。
使用超级调用进行组合
超级调用通常可用于组合操作。考虑以下示例:
abstract class A { def op() {} }
class X extends A {
var x: Int = 0
override def op() { x += 1 }
}
trait T extends A {
var y: String = "y"
override def op() { super.op(); y += "y" }
}
val xt = new X with T
println(s"${xt.x}, ${xt.y}") // 0, y
xt.op()
println(s"${xt.x}, ${xt.y}") // 1, yy
Let X.op
的属性为“x
增加一”并让T.op
的属性为“y
的长度增加了 1”。通过超级调用实现的构图满足了这两个属性。万岁!
你的问题
假设您正在与一个班级一起工作A
其中有一个字段x
, 一个特质T1
其中有一个字段y
还有另一个特点T2
其中有一个字段z
。您想要的是以下内容:
val obj: A with T1 with T2
// update obj's fields
val objC: A with T1 with T2 = obj.copy()
assert(obj.x == objC.x && obj.y == objC.y && obj.z == objC.z)
您的问题可以分为两个与组合性相关的子问题:
Create所需类型的新实例。这应该通过以下方式实现:construct
method.
初始化新创建的对象,使其所有字段都具有与源对象相同的值(为简洁起见,我们只使用值类型字段,而不使用引用类型字段)。这应该通过以下方式实现:initialise
method.
第二个问题可以通过超级调用解决,第一个问题不能。我们将首先考虑更简单的问题(第二个)。
对象初始化
我们假设construct
方法按需要工作并产生正确类型的对象。类似于op
我们可以实现初始示例中的方法initialise
这样每个类别/特征A
, T1
and T2
实施initialise(objC)
通过将其了解的字段设置为相应的值this
(个人效果),并通过调用super.initialise(objC)
为了合成这些单独的效果。
对象创建
据我所知,没有办法组合对象创建。如果一个实例A with T1 with T2
是要创建的,那么语句new A with T1 with T2
必须在某个地方执行。如果超级调用可以在这里提供帮助,那么类似
val a: A = new A // corresponds to the "deepest" super-call
val at1: A with T1 = a with new T1
将是必要的。
可能的解决方案
我实施了一个解决方案(看到这个要点 https://gist.github.com/anonymous/5037918)基于抽象类型成员和显式混合类(class AWithT1WithT2 extends A with T1 with T2; val a = new AWithT1WithT2
代替val a = new A with T1 with T2
)。它可以工作并且类型安全,但它既不是特别好也不是简洁。显式 mixin 类是必要的,因为construct
的方法new A with T1 with T2
必须能够命名它创建的类型。
其他类型安全性较低的解决方案可能也是可能的,例如,通过铸造asInstanceOf
或反思。不过,我还没有尝试过类似的事情。
Scala 宏也可能是一种选择,但我还没有使用过它们,因此对它们了解不够。编译器插件可能是另一个重量级选项。