7

It seems that the most common use of Scala's explicitly-typed self references is in the "Cake pattern," in which a module's dependencies are declared like:

class Foo { this: A with B with C =>
  // ...
}

In general, ignoring the cake pattern for a moment, A, B and C can refer to any type-level thing, such as a type parameter:

class Outer[A, B, C] {
  class Inner { this: A with B with C =>
    // ...
  }
}

... or an abstract type member:

class Outer {
  type A
  type B
  type C
  class Inner { this: A with B with C =>
    // ...
  }
}

In neither of these cases could we have written abstract class Inner extends A with B with C, because A, B and C are not known to be traits. The explicitly-typed self references are necessary here. However, I've only ever seen the cake pattern done with traits:

trait A { def a }
trait B { def b }
trait C { def c }
class Foo { this: A with B with C =>
  // ...
}

In this case, we can instead write abstract class Foo extends A with B with C directly, which if I'm not mistaken has the same meaning. Am I correct? If not, then how do they differ; and if so, why does everybody seem to use the explicitly-typed self reference regardless?

mergeconflict
  • 8,156
  • 34
  • 63
  • possible duplicate of [What is the difference between scala self-types and trait subclasses?](http://stackoverflow.com/questions/1990948/what-is-the-difference-between-scala-self-types-and-trait-subclasses) – kiritsuku Jul 13 '12 at 08:09
  • 1
    @sschaef different question there, but one of Daniel's comments under his answer might be the answer I'm looking for... Seems to be analogous maybe to `public` inheritance vs. `protected` inheritance in C++? – mergeconflict Jul 13 '12 at 17:47
  • For me both questions seem to want to know the same thing - they differ in abstract classes vs traits which is the same direction. Or did I fundamentally understand something wrong? – kiritsuku Jul 13 '12 at 18:04
  • Btw, I don't know C++. Can't say if there is a analogy to its public/protected inheritance. – kiritsuku Jul 13 '12 at 18:05

2 Answers2

6

It seems like there are two main differences I overlooked:

  1. Although both the explicit self-type annotation and the simple extends keyword describe an "is-a" relationship between two types, that relationship is not externally visible in the former case:

    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.
    

    This is a good thing, because in the cake pattern you don't want your "module" object to be inadvertently, polymorphically used as one of the traits on which it depends.

  2. As noted explicitly by Mushtaq and indirectly by Daniel, dependencies can be cyclic when using self-type annotations. Cyclic dependencies are very common, and not necessarily bad (assuming the interdependent components don't require each other for initialization, or that we can somehow tie the knot between them), so this is another clear benefit of self-type annotations over inheritance.

Community
  • 1
  • 1
mergeconflict
  • 8,156
  • 34
  • 63
1

When you use inheritance, you make decisions about initialization order. When you use self types, you leave that open.

There are other differences, but I think most of them are implementation details and may disappear. I know some of them are.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • 1
    I'd have preferred "when you use inheritance, God kills a kitten." – mergeconflict Jul 13 '12 at 19:27
  • On a more serious note... I don't understand why you mention path-dependent types here; do they make a difference in the choice between using `extends` vs. self types? – mergeconflict Jul 13 '12 at 19:49
  • @mergeconflict Thinking back, the classes actually being extended are often top level -- the nested ones are usually referred to inside them. I think I'm going to drop that from the answer, but the initialization order is a concern. There are other differences, but I have been corrected before by higher powers to the effect that many of them are implementation details instead of by design. – Daniel C. Sobral Jul 14 '12 at 01:00