据我所知,Scala线性化函数算法是先右深度优先搜索,然后在结果列表中消除除最后一个特征之外的所有特征。
这个算法有两个方面对我来说似乎相当随意:
这些任意约定是否与其他约定一样好?或者这些设计决策背后有理由吗?
请注意,我的问题仅涉及算法的“水平”排序。
答案 0 :(得分:9)
这些任意约定是否与其他约定一样好?
不,这些都不是随意的。线性化是以这种方式完成的,因为它是唯一可以为您提供理智的方法解析顺序(MRO)的方法。
为什么首先探索性状而不是类,而不是反过来呢?
这仅适用于给定的类定义,因为它扩展的特征在它扩展的类之前。这是因为从右到左探索超类/特征 - 这将我们带到了你的第二点。
为什么特征是从右到左遍历,而不是从左到右?
这是一个非常简单的激励示例。
type MyType = T1 with T2
// Creating new instance using alias
new MyType with T3
// Creating new instance directly
new T1 with T2 with T3
我们希望上面示例中的两个实例具有相同的线性化。当使用类型别名时,我们在右侧“堆叠”其他特征,因此我们期望右侧的特征在MRO中具有最高优先级,因此在线性化中首先出现。 / p>
这是我想出的一个简单示例,说明了特征和类的线性化:
class Base { override def toString = "Base" }
trait A { abstract override def toString = "A " + super.toString }
trait B { abstract override def toString = "B " + super.toString }
trait C { abstract override def toString = "C " + super.toString }
trait D { abstract override def toString = "D " + super.toString }
trait E { abstract override def toString = "E " + super.toString }
trait F { abstract override def toString = "F " + super.toString }
class X extends Base with A with B { override def toString = "X " + super.toString }
class Y extends X with C with D { override def toString = "Y " + super.toString }
class Z extends Y with E with F { override def toString = "Z " + super.toString }
new Z
// res0: Z = Z F E Y D C X B A Base
您可以从toString
输出中看到此处的线性化为Z F E Y D C X B A Base
,这正是我对给定层次结构所期望的。例如,由于Z
扩展Y
,混合了特征C
,我们希望Y
的行为在C
之前出现,但之后Z
及其混合E
和F
。
关于此Chapter 12 of Programming in Scala, First Edition: Traits的简单参考。 (我认为最近这种情况没有改变,所以第一版应该仍然准确。)