Jay Taylor's notes

back to listing index

functional programming - What is the difference between scala self-types and trait subclasses? - Stack Overflow

[web search]
Original source (stackoverflow.com)
Tags: scala manifests self stackoverflow.com
Clipped on: 2012-09-25

Self-types seem to be important so I want to know why they are useful. From what I can gather, a self-type for a trait A:

trait B
trait A { this: B => }

says that "A cannot be mixed into a concrete class that does not also extend B".

(sidenote: I'm also seeing this fail in the REPL when mixed into an abstract class that does not extend B, which the book "Programming in Scala" says should work, but I'm using a Scala 2.8 daily build.)

But if I say:

trait B
trait A extends B

then this means that "any (concrete or abstract) class mixing in A will also be mixing in B". But don't these two statements mean the same thing? The self-type seems to serve only to create the possibility of a simple compile-time error.

I have considered that the difference may be the order of function overloading when using a self-type instead of an extends clause... but this seems like a minor distinction, not worthy of the hoopla over self-types. What am I missing?

asked Jan 2 '10 at 8:07
Image (Asset 1/10) alt= 7401227

67% accept rate
  upvote
 flag
I'm actually interested here in the differences between self types and subclassing in traits. I do know some of the common uses for self-types; I just can't find a reason why they wouldn't be more clearly done the same way with subtyping. – Dave Jan 3 '10 at 2:33
add comment

7 Answers

up vote 36 down vote accepted

It is used for Dependency Injection, such as in the Cake Pattern. I once read a great article covering many different forms of dependency injection in Scala, including the Cake Pattern, but I can't find it right now (edit: thanks to Mushtaq, it is now linked). If you look up google for Cake Pattern and Scala, you'll get many links, including presentations and videos. For now, there's a publicly-available chapter of O'Reilly's Programming Scala book with a section on it.

Now, as to what is the difference between a self type and extending a trait, that is simple. If you say B extends A, then B is an A. When you do dependency injection, you want B to require A, not to be an A. For example:

scala> trait User { def name: String }
defined
trait User

scala
> trait Tweeter {
     
|   user: User =>
     
|   def tweet(msg: String) = println(name+": "+msg)
     
| }
defined
trait Tweeter

scala
> trait Wrong extends Tweeter {
     
|   def noCanDo = name
     
| }
<console>:8: error: illegal inheritance;
 self
-type Wrong does not conform to Tweeter's selftype Tweeter with User
       
trait Wrong extends Tweeter {
                           
^
$iw
.$iw.Wrong <: $iw.$iw.Tweeter with $iw.$iw.User?
  $iw
.$iw.Wrong <: $iw.$iw.Tweeter?
 
true
  $iw
.$iw.Wrong <: $iw.$iw.User?
   
<notype> <: $iw.$iw.User?
   
false
 
false
false
<console>:9: error: not found: value name
         
def noCanDo = name
                       
^

Which would cause no error if subclassing were used.

Image (Asset 2/10) alt= 14.9k41855
answered Jan 2 '10 at 14:42
Image (Asset 3/10) alt= 99.2k13174358
1 upvote
 flag
I guess you are referring to this article by Jonas Boner: (jonasboner.com/2008/10/06/…) – Mushtaq Ahmed Jan 2 '10 at 18:36
2 upvote
 flag
Thanks. The Cake pattern is 90% of what I mean why I talk about the hype around self-types... it is where I first saw the topic. Jonas Boner's example is great because it underscores the point of my question. If you changed the self-types in his heater example to be subtraits then what would be the difference (other than the error you get when defining the ComponentRegistry if you don't mix in the right stuff? – Dave Jan 3 '10 at 2:40
  upvote
 flag
That's the one, Mushtaq. Thanks. – Daniel C. Sobral Jan 3 '10 at 4:04
4 upvote
 flag
@Dave: You mean like trait WarmerComponentImpl extends SensorDeviceComponent with OnOffDeviceComponent? That would cause WarmerComponentImpl to have those interfaces. They would be available to anything that extended WarmerComponentImpl, which is clearly wrong, as it is not a SensorDeviceComponent, nor a OnOffDeviceComponent. As a self type, these dependencies are available exclusively to WarmerComponentImpl. A List could be used as an Array, and vice versa. But they just aren't the same thing. – Daniel C. Sobral Jan 3 '10 at 4:14
  upvote
 flag
Thanks Daniel. This is probably the major distinction I was looking for. The practical problem is that using subclassing will leak functionality into your interface that you don't intend. Its a result of the violation of the more theoretical "is-part-of-a" rule for traits. Self-types express a "uses-a" relationship between parts. – Dave Jan 3 '10 at 6:16
add / show 6 more comments

in the first case, a sub-trait or sub-class of B can be mixed in to whatever uses A. So B can be an abstract trait.

answered Jan 2 '10 at 10:03
Image (Asset 4/10) alt= 7,17032254
add comment

A self type lets you specify what types are allowed to mixin a trait. For example, if you have a trait with a self type "Closeable", then that trait knows that the only things that are allowed to mix it in, must implement the Closeable interface.

answered Jan 2 '10 at 11:20
Image (Asset 5/10) alt= 30725
1 upvote
 flag
That's incorrect, other traits can be also mixed in. – Blaisorblade Aug 20 '11 at 17:45
add comment

Self types allow you to define cyclical dependencies. For example you can achieve this:

trait A {self: B=>}
trait B {self: A=>}

Inheritance using extends does not allow that. Try:

trait A extends B
trait B extends A
error
:  illegal cyclic reference involving trait A

In the Odersky book, look at section 33.5 (Creating spreadsheet UI chapter) where it mentions:

In the spreadsheet example, class Model inherits from Evaluator and thus gains access to its evaluation method. To go the other way, class Evaluator defines its self type to be Model, like this:

package org.stairwaybook.scells
trait Evaluator { this: Model => ...

Hope this helps.

answered Jan 2 '10 at 11:43
Image (Asset 6/10) alt= 1,07548
1 upvote
 flag
I hadn't considered this scenario. Its the first example of something that I've seen that isn't the same as a self-type as it is with a subclass. However, it seems kind of edge-casey and, more important, it seems like a bad idea (I usually go far out of my way NOT to define cyclic dependencies!). Do you find this to be the most important distinction? – Dave Jan 3 '10 at 2:36
1 upvote
 flag
I think so. I do not see any other reason why I would prefer self-types to extends clause. Self-types are verbose, they do not get inherited (so you have to add self-types to all subtypes as a ritual) and you can only see member but can't override them. I am well aware of Cake pattern and many posts mentioning self-types for DI. But somehow I am not convinced. I had created a sample app here long back (bitbucket.org/mushtaq/scala-di). Look specifically at /src/configs folder. I achieved DI to replace complex Spring configurations without self-types. – Mushtaq Ahmed Jan 3 '10 at 13:19
  upvote
 flag
Mushtaq, we're in agreement. I think Daniel's statement about not exposing unintentional functionality is an important one but, as you put it, there is a mirror view of this 'feature'... that you cannot override the functionality or use it in future subclasses. This pretty clearly tells me when design will call for one over the other. I'll be avoiding self-types until I find a genuine need -- ie if I start using objects as modules as Daniel points out. I'm autowiring dependencies with implicit parameters and a straightforward bootstrapper object. I like the simplicity. – Dave Jan 4 '10 at 1:35
1 upvote
 flag
This answer is so underrated... It is a very good point I had missed myself. It is sad that it still lingers with 3 upvotes after a year. – Daniel C. Sobral Jan 19 '11 at 11:08
  upvote
 flag
@DanielC.Sobral may be thanks to your comment but at the moment it has more upvotes than your anser. Upvoting both :) – rintcius Sep 14 at 17:41
add comment

One additional difference is that self-types can specify non-class types. For instance

trait Foo{
   
this => {def close:Unit}
}

The self type here is a structural type. The effect is to say that anything that mixes in Foo must implement a no-arg "close" method returning unit. This allows for safe mixins for duck-typing.

Image (Asset 7/10) alt= 16.4k33180
answered Jun 21 '10 at 15:04
Image (Asset 8/10) alt= 9,20311841
5 upvote
 flag
Actually you can use inheritance with structural types too: abstract class A extends {def close:Unit} – Adrian Mar 22 '11 at 2:14
add comment

Check the following article it containes very simple but real word examples that made me understand basic of self-types in a minute.

answered Nov 12 '11 at 11:08
Image (Asset 9/10) alt= 65267
add comment

Scala self-types are not very flexible because you cannot have:

trait B
trait A { this: B => }
trait AbstractFancyA extends A

as AbstractFancyA exhibits illegal inheritance, but you can of course have the equivalent:

trait B
trait A extends B
trait AbstractFancyA extends A

Yes, admittedly you can have mutually-recursive dependencies with self-types - but how often do you need something like that?

answered Sep 13 at 17:43
Image (Asset 10/10) alt= 3,529733
add comment

Your Answer

 
community wiki

Not the answer you're looking for? Browse other questions tagged or ask your own question.