Scala线性化函数的基本原理

时间:2013-11-06 08:31:07

标签: scala traits

据我所知,Scala线性化函数算法是先右深度优先搜索,然后在结果列表中消除除最后一个特征之外的所有特征。

这个算法有两个方面对我来说似乎相当随意:

  • 为什么首先探索性状而不是类,而不是反过来呢?
  • 为什么特征是从右到左遍历,而不是从左到右?

这些任意约定是否与其他约定一样好?或者这些设计决策背后有理由吗?

请注意,我的问题仅涉及算法的“水平”排序。

1 个答案:

答案 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及其混合EF

关于此Chapter 12 of Programming in Scala, First Edition: Traits的简单参考。 (我认为最近这种情况没有改变,所以第一版应该仍然准确。)