蛋糕模式中显式类型自引用的有用性

mer*_*ict 7 scala cake-pattern

似乎Scala的显式类型自引用最常见的用法是" Cake模式 ",其中模块的依赖项声明如下:

class Foo { this: A with B with C =>
  // ...
}
Run Code Online (Sandbox Code Playgroud)

通常,暂时忽略蛋糕模式A,B并且C可以引用任何类型级别的东西,例如类型参数:

class Outer[A, B, C] {
  class Inner { this: A with B with C =>
    // ...
  }
}
Run Code Online (Sandbox Code Playgroud)

......或抽象类型的成员:

class Outer {
  type A
  type B
  type C
  class Inner { this: A with B with C =>
    // ...
  }
}
Run Code Online (Sandbox Code Playgroud)

在这些情况中,我们都不能写abstract class Inner extends A with B with C,因为A,B并且C不知道是特征.这里需要明确键入的自引用.但是,我只见过用特征完成的蛋糕模式:

trait A { def a }
trait B { def b }
trait C { def c }
class Foo { this: A with B with C =>
  // ...
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我们可以abstract class Foo extends A with B with C直接写,如果我没有弄错,它具有相同的含义.我对么?如果没有,那么他们如何区别; 如果是这样,为什么每个人似乎都使用明确类型的自引用?

mer*_*ict 6

似乎我忽略了两个主要的区别:

  1. 虽然显式自我类型注释和简单extends关键字都描述了两种类型之间的"is-a"关系,但在前一种情况下,该关系在外部并不可见:

    scala> trait T
    defined trait T
    
    scala> class C { this: T => }
    defined class C
    
    scala> implicitly[C <:< T]
    <console>:10: error: Cannot prove that C <:< T.
    
    Run Code Online (Sandbox Code Playgroud)

    这是一件好事,因为在蛋糕模式中你不希望你的"模块"对象无意中被多态地用作它所依赖的特征之一.

  2. 如明确穆什塔克指出由Daniel间接,依赖关系可以使用自式注释时,是环状的.循环依赖是非常常见的,并且不一定是坏的(假设相互依赖的组件不需要彼此进行初始化,或者我们可以以某种方式它们绑定在它们之间),所以这是自我类型注释相对于继承的另一个明显好处.