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".
On the other hand, the following:
trait B
trait A extends B
says that "any (concrete or abstract) class mixing in A will also be mixing in B".
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.
What am I missing?
It is predominately used for Dependency Injection, such as in the Cake Pattern. There exists a great article covering many different forms of dependency injection in Scala, including the Cake Pattern. If you Google "Cake Pattern and Scala", you'll get many links, including presentations and videos. For now, here is a link to another question.
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 use self-types, B requires an A. There are two specific requirements that are created with self-types:
If B is extended, then you're required to mix-in an A.
When a concrete class finally extends/mixes-in these traits, some class/trait must implement A.
Consider the following examples:
scala> trait User { def name: String }
defined trait User
scala> trait Tweeter {
| user: User =>
| def tweet(msg: String) = println(s"$name: $msg")
| }
defined trait Tweeter
scala> trait Wrong extends Tweeter {
| def noCanDo = name
| }
<console>:9: error: illegal inheritance;
self-type Wrong does not conform to Tweeter's selftype Tweeter with User
trait Wrong extends Tweeter {
^
<console>:10: error: not found: value name
def noCanDo = name
^
If Tweeter was a subclass of User, there would be no error. In the code above, we required a User whenever Tweeter is used, however a User wasn't provided to Wrong, so we got an error. Now, with the code above still in scope, consider:
scala> trait DummyUser extends User {
| override def name: String = "foo"
| }
defined trait DummyUser
scala> trait Right extends Tweeter with User {
| val canDo = name
| }
defined trait Right
scala> trait RightAgain extends Tweeter with DummyUser {
| val canDo = name
| }
defined trait RightAgain
With Right, the requirement to mix-in a User is satisfied. However, the second requirement mentioned above is not satisfied: the burden of implementing User still remains for classes/traits which extend Right.
With RightAgain both requirements are satisfied. A User and an implementation of User are provided.
For more practical use cases, please see the links at the start of this answer! But, hopefully now you get it.
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 => ...
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.
Another thing that has not been mentioned: because self-types aren't part of the hierarchy of the required class they can be excluded from pattern matching, especially when you are exhaustively matching against a sealed hierarchy. This is convenient when you want to model orthogonal behaviors such as:
sealed trait Person
trait Student extends Person
trait Teacher extends Person
trait Adult { this : Person => } // orthogonal to its condition
val p : Person = new Student {}
p match {
case s : Student => println("a student")
case t : Teacher => println("a teacher")
} // that's it we're exhaustive
Section 2.3 "Selftype Annotations" of Martin Odersky's original Scala paper Scalable Component Abstractions actually explains the purpose of selftype beyond mixin composition very well: provide an alternative way of associating a class with an abstract type.
The example given in the paper was like the following, and it doesn't seem to have an elegant subclass correspondent:
abstract class Graph {
type Node <: BaseNode;
class BaseNode {
self: Node =>
def connectWith(n: Node): Edge =
new Edge(self, n);
}
class Edge(from: Node, to: Node) {
def source() = from;
def target() = to;
}
}
class LabeledGraph extends Graph {
class Node(label: String) extends BaseNode {
def getLabel: String = label;
def self: Node = this;
}
}
TL;DR summary of the other answers:
Types you extend are exposed to inherited types, but self-types are not
eg: class Cow { this: FourStomachs } allows you to use methods only available to ruminants, such as digestGrass. Traits that extend Cow however will have no such privileges. On the other hand, class Cow extends FourStomachs will expose digestGrass to anyone who extends Cow .
self-types allow cyclical dependencies, extending other types does not
Let's start with the cyclical dependency.
trait A {
selfA: B =>
def fa: Int }
trait B {
selfB: A =>
def fb: String }
However, the modularity of this solution is not as great as it might first appear, because you can override self types as so:
trait A1 extends A {
selfA1: B =>
override def fb = "B's String" }
trait B1 extends B {
selfB1: A =>
override def fa = "A's String" }
val myObj = new A1 with B1
Although, if you override a member of a self type, you lose access to the original member, which can still be accessed through super using inheritance. So what is really gained over using inheritance is:
trait AB {
def fa: String
def fb: String }
trait A1 extends AB
{ override def fa = "A's String" }
trait B1 extends AB
{ override def fb = "B's String" }
val myObj = new A1 with B1
Now I can't claim to understand all the subtleties of the cake pattern, but it strikes me that the main method of enforcing modularity is through composition rather than inheritance or self types.
The inheritance version is shorter, but the main reason I prefer inheritance over self types is that I find it much more tricky to get the initialisation order correct with self types. However, there are some things you can do with self types that you can't do with inheritance. Self types can use a type while inheritance requires a trait or a class as in:
trait Outer
{ type T1 }
trait S1
{ selfS1: Outer#T1 => } //Not possible with inheritance.
You can even do:
trait TypeBuster
{ this: Int with String => }
Although you'll never be able to instantiate it. I don't see any absolute reason for not being be able to inherit from a type, but I certainly feel it would be useful to have path constructor classes and traits as we have type constructor traits / classes. As unfortunately
trait InnerA extends Outer#Inner //Doesn't compile
We have this:
trait Outer
{ trait Inner }
trait OuterA extends Outer
{ trait InnerA extends Inner }
trait OuterB extends Outer
{ trait InnerB extends Inner }
trait OuterFinal extends OuterA with OuterB
{ val myV = new InnerA with InnerB }
Or this:
trait Outer
{ trait Inner }
trait InnerA
{this: Outer#Inner =>}
trait InnerB
{this: Outer#Inner =>}
trait OuterFinal extends Outer
{ val myVal = new InnerA with InnerB with Inner }
One point that should be empathised more is that traits can extends classes. Thanks to David Maclver for pointing this out. Here's an example from my own code:
class ScnBase extends Frame
abstract class ScnVista[GT <: GeomBase[_ <: TypesD]](geomRI: GT) extends ScnBase with DescripHolder[GT] )
{ val geomR = geomRI }
trait EditScn[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]
trait ScnVistaCyl[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]
ScnBase inherits from the Swing Frame class, so it could be used as a self type and then mixed in at the end (at instantiation). However, val geomR needs to be initialised before it's used by inheriting traits. So we need a class to enforce prior initialisation of geomR. The class ScnVista can then be inherited from by multiple orthogonal traits which can themselves be inherited from. Using multiple type parameters (generics) offers an alternative form of modularity.
trait A { def x = 1 }
trait B extends A { override def x = super.x * 5 }
trait C1 extends B { override def x = 2 }
trait C2 extends A { this: B => override def x = 2}
// 1.
println((new C1 with B).x) // 2
println((new C2 with B).x) // 10
// 2.
trait X {
type SomeA <: A
trait Inner1 { this: SomeA => } // compiles ok
trait Inner2 extends SomeA {} // doesn't compile
}
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.
Update: A principal difference is that self-types can depend on multiple classes (I admit that's a bit corner case). For example, you can have
class Person {
//...
def name: String = "...";
}
class Expense {
def cost: Int = 123;
}
trait Employee {
this: Person with Expense =>
// ...
def roomNo: Int;
def officeLabel: String = name + "/" + roomNo;
}
This allows to add the Employee mixin just to anything that is a subclass of Person and Expense. Of course, this is only meaningful if Expense extends Person or vice versa. The point is that using self-types Employee can be independent of the hierarchy of the classes it depends on. It doesn't care of what extends what - If you switch the hierarchy of Expense vs Person, you don't have to modify Employee.
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.
Related
I recently discovered that Scala compiler has an interesting feature for case class: Since it generates both a class & an object signature, if defined as an inner class, it can be used to override an abstract type definition and a function definition of its super class with minimal boilerplate code, here is an example:
object InnerCaseClassOverridingBoth {
trait AALike
trait SS {
type AA <: AALike
def AA(): AnyRef
}
trait SS_Clear extends SS {
def AA(): AnyRef
}
class SSA extends SS_Clear {
case class AA() extends AALike
}
object SSA extends SSA {}
}
This will compile without any error. However the shortcut stops here, if the function definition def AA is parameterized, then neither the inner case class nor inner object is capable of overriding it: the apply function of the inner object doesn't automatically expand to a method of its outer class:
trait SS_Parameterised extends SS {
def AA(ii: Int): AnyRef
}
class SSB extends SS_Parameterised {
case class AA(ii: Int) extends AALike
}
object SSB extends SSB {}
This gives an error:
class SSB needs to be abstract, since method AA in trait
SS_Parameterised of type (ii: Int)AnyRef is not defined
class SSB extends SS_Parameterised {
My question is, is there a shortcut in this case? Why is the Scala compiler is designed to link case 1 but not case 2?
It's not particularly designed at all; or, it is, but not in the way you seem to think. You aren't overriding def AA() with a method that constructs AA, you are overriding it with the object AA itself. Notice
trait T {
type I <: AnyRef
def I(): AnyRef
}
object O extends T {
case class I(val i: Int)
}
This works fine.
> (O: T).I()
I
> (O: T).I().getClass
class O$I$
> O.I(5)
I(5)
> O.I(5).getClass
class O$I
The salient design choices are "objects can override no-param defs" (and so can vals, vars and, of course, no-param defs) and "case classes automatically generate objects". "Inner case classes override methods of the same name in their outer class with their constructors," is not one of Scala's rules. object O contains a case class I and an object I, and the abstract def I(): AnyRef is overridden to return said object I. The contents of object I don't matter, because def I() only has to return an AnyRef, which means no restrictions are imposed. It makes perfect sense that
trait U {
type I <: AnyRef
def I(i: Int): AnyRef
}
object P extends U {
case class I(i: Int)
}
fails, then. object P contains a case class I and an associated object I, but it also needs a def I(i: Int): AnyRef, which it lacks.
I am guessing it is simply related to the role apply plays in case classes. See Case Class default apply method
SSA satisfies SS_Clear.AA via companion object of SSA (SSA.apply).
When you add a parameter to the method you no longer have the 0-parameter apply method to fulfill that role.
OK I found 2 ways of doing this
Method 1: overriden by case class:
trait SS_Parameterised {
type AA <: AALike
def AA: Int => AnyRef
}
Method 2: overriden by implicit class:
trait SS_Parameterised {
type AA <: AALike
implicit def AA(ii: Int): AnyRef
}
class SSB extends SS_Parameterised {
implicit class AA(ii: Int) extends AALike
}
End of story :) One case class overriding 2 declarations? No problem.
(Method 2 works as scala internally generates an implicit function for every implicit class)
Given a concrete class Animal, how do I define a function that only takes a subclass of Animal?
In typical examples like this Animal is a trait so defining [A <: Animal] implies that you already pass in a subclass of Animal. However, in a scenario like below where Animal is concrete, can I exclude that as being an allowed type?
I'm working with existing generated code, and this is just a generalized example of the problem. Therefore the implication is that I can't make Animal (or the equivalent) into a trait.
See below for an example:
class Animal {
def name: String = "General Animal"
}
class Dog extends Animal {
override def name: String = "Dog"
}
// How do I limit A to be a subtype of Animal (excluding Animal itself)?
class SpecificAnimalContainer[A <: Animal](a: A) {
def specificAnimal: A = a
}
val dogContainer = new SpecificAnimalContainer[Dog](new Dog)
// I do not want this to be able to compile.
val animalContainer = new SpecificAnimalContainer[Animal](new Animal)
Using shapeless you can write:
import shapeless._
class SpecificAnimalContainer[A <: Animal](a: A)(implicit ev: A =:!= Animal) {
def specificAnimal: A = a
}
// val animalContainer = new SpecificAnimalContainer[Animal](new Animal)// doesn't compile
Otherwise you can implement similar type for implicit yourself.
Type constraint for type inequality in scala
Enforce type difference
How can I have a negation type in Scala?
It's a bit unclear what you're trying to achieve, but your problem looks exactly like a book example from Scala documentation at
https://docs.scala-lang.org/tour/upper-type-bounds.html
abstract class Pet extends Animal {}
class PetContainer[P <: Pet](p: P) {
def pet: P = p
}
class Lion extends Animal {
override def name: String = "Lion"
}
// val lionContainer = new PetContainer[Lion](new Lion)
// ^this would not compile
Hope this helps
I want to to extend a trait within a trait, like this:
trait NodeTypes {
trait Node {
def allNodesHaveThis: Int
}
}
trait ScrumptiousTypes extends NodeTypes {
trait Node extends super.Node {
def scrumptiousness: Int
}
}
trait YummyTypes extends NodeTypes {
trait Node extends super.Node {
def yumminess: Int
}
}
object Graph extends NodeTypes with ScrumptiousTypes with YummyTypes {
case class Node() extends super.Node {
override def allNodesHaveThis = 1
override def scrumptiousness = 2 // error: ScrumptiousTypes.Node has been disinherited
override def yumminess = 3
}
}
If this works, it would be a nice way of saying “When your Graph inherits from <Whatever>Types, its Node class must provide the methods required by <Whatever>.”
But the Scala 2.11.2 compiler says:
error: method scrumptiousness overrides nothing
override def scrumptiousness = 2
^
It appears that YummyTypes.Node shadows ScrumptiousTypes.Node, following the usual way that Scala resolves “diamond” inheritance for methods: by type linearization. As I understand things, that should be OK, though, because YummyTypes.Node explicitly extends super.Node, which, by the same type linearization, should refer to ScrumptiousTypes.
What have I misunderstood? Or, what does super.Node refer to—and why?
If you're wondering why I'm doing this, it's so I can mix changes into several traits at once, so the inherited traits interoperate, as explained in this question. In the final Node class (and other classes that it works with), I don't want to explicitly extend from each Node trait: I want to mix in from one "thing" (whatever it is) and get all the mutually consistent changes made to Node and the other traits, all in a bundle. Or, if one trait defines a bunch of extensions to Node, extending from ScrumptiousTypes should make all of the Node-extensions contain a scrumptiousness member, without having to list all the Node-extensions: trait Hypernode extends ScrumptiousTypes.Node, trait ZealousNode extends ScrumptiousTypes.Node, etc.
use type also fix the issue
trait NodeTypes {
trait Node {
def allNodesHaveThis: Int
}
}
trait ScrumptiousTypes extends NodeTypes {
trait Node extends super.Node {
def scrumptiousness: Int
}
type ScrumptiousTypesNode = this.Node
}
trait YummyTypes extends NodeTypes {
trait Node extends super.Node {
def yumminess: Int
}
type YummyTypesNode = this.Node
}
object Graph extends NodeTypes with ScrumptiousTypes with YummyTypes {
case class Node() extends ScrumptiousTypesNode with YummyTypesNode {
override def allNodesHaveThis = 1
override def scrumptiousness = 2
override def yumminess = 3
}
}
------v2-------
use object contain to Node ,
but since path depend it is not a good idea ,
and maybe It will be problems
trait NodeTypes {
trait Node {
def allNodesHaveThis: Int
}
}
object NodeTypes extends NodeTypes
trait ScrumptiousTypes extends NodeTypes {
trait Node {
def scrumptiousness: Int
}
type ScrumptiousTypesNode = this.Node
}
object ScrumptiousTypes extends ScrumptiousTypes
trait YummyTypes extends NodeTypes {
trait Node {
def yumminess: Int
}
type YummyTypesNode = this.Node
}
object YummyTypes extends YummyTypes
trait Nodes {
trait Nodes extends NodeTypes.Node with YummyTypes.Node with ScrumptiousTypes.Node
}
object Graph extends Nodes {
case class Nodes() extends super.Nodes {
override def yumminess: Int = 1
//
override def scrumptiousness: Int = 2
override def allNodesHaveThis: Int = 3
}
}
This is because of class lineraization. See Spec.
Explanation
Let C be a class with template C1 with ... with Cn. Then lineraization is concatenation of elements from Cn to C1, replacing all identical elements to left. Here elements include var, val, def, traits, object.
If you want to see the order of linearization, use
import scala.reflect.runtime.universe._
val tpe = typeOf[scala.collection.immutable.List[_]]
tpe.baseClasses foreach { s => println(s.fullName) }
In your case, if you change the order from ScrumptiousTypes with YummyTypes to YummyTypes with ScrumptiousTypes, then error will be on method yumminess.
An alternate to #余杰水 is to extend inner class like,
case class Node() extends super[ScrumptiousTypes].Node with super[YummyTypes].Node
This isn't meant to be an answer. It's just quotations from and interpretations of the spec, which are too long to fit readably into comments (prompted by johny's answer). I'm spelling out my interpretations so you might be able to spot where I went wrong. Maybe this will lead to an explanation or to a way to chain extensions of traits within traits (or to a bug report in the unlikely event that my interpretation turns out to be right).
Relevant passages from the Scala spec
§6.5 This and Super: A reference super.m refers statically to a method or type m in the least proper supertype of the innermost template containing the reference. It evaluates to the member m′ in the actual supertype of that template which is equal to m or which overrides m.
The big question is: What does the spec say that super.Node inside YummyTypes refers to? To find out, we'll need to know the definitions of the spec-specific terms used above:
§5.1 Templates: A template defines the type signature, behavior and initial state of a trait or class of objects or of a single object.
So, a template is what we'd ordinarily call an object, class, or trait definition.
§5.4 Traits: A trait is a class that is meant to be added to some other class as a mixin. … The least proper supertype of a template is the class type or compound type consisting of all its parent class types.
§5.1.2 Class Linearization: Let C be a class with template C1 with ... with Cn. The linearization of C, L(C), is defined as follows:
L(C) = C,L(Cn) +⃗ … +⃗ L(C1)
Here +⃗ denotes concatenation where elements of the right operand replace identical elements of the left operand.
I take this to mean that the linearization is a sequence of classes, which you get by starting with the class being defined and then reading the with types from right to left. When two classes in the linearization define a member or type with the same name (an “element”), the class that comes first “wins”.
So, the linearization of Graph should be Graph,YummyTypes,ScrumptiousTypes,NodeTypes, followed by standard stuff like Any. Indeed, this is confirmed when I modify Graph like this:
object Graph extends ScrumptiousTypes with YummyTypes {
case class Node() extends super.Node { /* … */ }
typeOf[Graph.type].baseClasses foreach { s => println(s.fullName) }
}
which produces:
Graph
YummyTypes
ScrumptiousTypes
NodeTypes
java.lang.Object
scala.Any
§5.4 Traits: Assume a trait D defines some aspect of an instance x of type C (i.e. D is a base class of C). Then the actual supertype of D in x is the compound type consisting of all the base classes in L(C) that succeed D. The actual supertype gives the context for resolving a super reference in a trait. Note that the actual supertype depends on the type to which the trait is added in a mixin composition; it is not statically known at the time the trait is defined.
I take this to mean that the "actual" least proper supertype of a mixed-in trait is determined by the type of the actual object that the trait is mixed into (Graph in my example), not necessarily a supertype that the trait's definition explicitly extends (NodeTypes in my example).
Conclusion
So, it would appear that the actual supertype of YummyTypes in Graph should be ScrumptiousTypes. And so, the actual supertype of YummyTypes.Node in Graph should be ScrumptiousTypes.Node.
However, adding this line to Graph:
typeOf[Node].baseClasses foreach { s => println(s.fullName) }
produces:
Graph.Node
scala.Serializable
java.io.Serializable
scala.Product
scala.Equals
YummyTypes.Node
NodeTypes.Node
java.lang.Object
scala.Any
ScrumptiousTypes.Node is missing. Apparently, inside YummyTypes, super.Node does not refer to Node in YummyTypes' actual least proper supertype.
However, if I add:
abstract override def text = super.text + " ScrumptiousTypes" // in ScrumptiousTypes
abstract override def text = super.text + " YummyTypes" // in YummyTypes
printing text in Graph produces:
ScrumptiousTypes YummyTypes
demonstrating that inside YummyTypes in Graph, super.text does refer to ScrumptiousTypes.text!
I've tried to come up with a composition scenario in which self-type and extends behave differently and so far have not found one. The basic example always talks about a self-type not requiring the class/trait not having to be a sub-type of the dependent type, but even in that scenario, the behavior between self-type and extends seems to be identical.
trait Fooable { def X: String }
trait Bar1 { self: Fooable =>
def Y = X + "-bar"
}
trait Bar2 extends Fooable {
def Y = X + "-bar"
}
trait Foo extends Fooable {
def X = "foo"
}
val b1 = new Bar1 with Foo
val b2 = new Bar2 with Foo
Is there a scenario where some form of composition or functionality of composed object is different when using one vs. the other?
Update 1: Thanks for the examples of things that are not possible without self-typing, I appreciate the information, but I am really looking for compositions where self and extends are possible, but are not interchangeable.
Update 2: I suppose the particular question I have is why the various Cake Pattern examples generally talk about having to use self-type instead of extends. I've yet to find a Cake Pattern scenario that doesn't work just as well with extends
Cyclic references can be done with self-types but not with extends:
// Legal
trait A { self: B => }
trait B { self: A => }
// Illegal
trait C extends D
trait D extends C
I use this sometimes to split up implementations across multiple files, when there are cyclic dependencies.
Also,
scala> trait A { def a: String ; def s = "A" }
defined trait A
scala> trait B { _: A => def s = "B" + a }
defined trait B
scala> trait C extends A { def a = "c" ; override def s = "C" }
defined trait C
scala> new C {}.s
res0: String = C
scala> new A with B { def a = "ab" }.s
<console>:10: error: <$anon: A with B> inherits conflicting members:
method s in trait A of type => String and
method s in trait B of type => String
(Note: this can be resolved by declaring an override in <$anon: A with B>.)
new A with B { def a = "ab" }.s
^
scala> new A with B { def a = "ab" ; override def s = super[B].s }.s
res2: String = Bab
The point, if there is one, is that B.s doesn't override A.s.
That's not as motivational as the other answer.
The generic parameter must be the type itself:
trait Gen[T] {self : T => ...}
I don't see how you can get this constraint in say java or C#. It may however be approximated with
trait Gen[T] {
def asT : T // abstract
}
Also,
as for self type, it needs a trait to mix in. It cannot use class or object. The weird thing is it allows to define a class can mix in with class, but it only fails compilation when you try to instantiate it. see this question:
why self-type class can declare class
The biggest difference is in the public interface that you end up with. Let's take the example you give (slightly simplified):
trait Fooable { def foo: String = "foo" }
trait Bar1 { self: Fooable =>
def Y = foo + "-bar"
}
trait Bar2 extends Fooable {
def Y = foo + "-bar"
}
// If we let type inference do its thing we would also have foo() in the public interface of b1, but we can choose to hide it
def b1:Bar1 = new Bar1 with Fooable
// b2 will always have the type members from Bar2 and Fooable
def b2:Bar2 = new Bar2{}
// Doesn't compile - 'foo' definition is only visible inside the definition of Bar1
println(b1.foo)
// Compiles - 'foo' definition is visible outside the definition of Bar2
println(b2.foo)
So if you want to use the capabilities of a trait without necessarily letting your clients know that you are mixing the trait in, then you should use the self-type annotation.
Self-type annotation does not expose the public interface of the underlying type.
Extending another type always exposes the public interface of the parent type.
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".
On the other hand, the following:
trait B
trait A extends B
says that "any (concrete or abstract) class mixing in A will also be mixing in B".
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.
What am I missing?
It is predominately used for Dependency Injection, such as in the Cake Pattern. There exists a great article covering many different forms of dependency injection in Scala, including the Cake Pattern. If you Google "Cake Pattern and Scala", you'll get many links, including presentations and videos. For now, here is a link to another question.
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 use self-types, B requires an A. There are two specific requirements that are created with self-types:
If B is extended, then you're required to mix-in an A.
When a concrete class finally extends/mixes-in these traits, some class/trait must implement A.
Consider the following examples:
scala> trait User { def name: String }
defined trait User
scala> trait Tweeter {
| user: User =>
| def tweet(msg: String) = println(s"$name: $msg")
| }
defined trait Tweeter
scala> trait Wrong extends Tweeter {
| def noCanDo = name
| }
<console>:9: error: illegal inheritance;
self-type Wrong does not conform to Tweeter's selftype Tweeter with User
trait Wrong extends Tweeter {
^
<console>:10: error: not found: value name
def noCanDo = name
^
If Tweeter was a subclass of User, there would be no error. In the code above, we required a User whenever Tweeter is used, however a User wasn't provided to Wrong, so we got an error. Now, with the code above still in scope, consider:
scala> trait DummyUser extends User {
| override def name: String = "foo"
| }
defined trait DummyUser
scala> trait Right extends Tweeter with User {
| val canDo = name
| }
defined trait Right
scala> trait RightAgain extends Tweeter with DummyUser {
| val canDo = name
| }
defined trait RightAgain
With Right, the requirement to mix-in a User is satisfied. However, the second requirement mentioned above is not satisfied: the burden of implementing User still remains for classes/traits which extend Right.
With RightAgain both requirements are satisfied. A User and an implementation of User are provided.
For more practical use cases, please see the links at the start of this answer! But, hopefully now you get it.
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 => ...
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.
Another thing that has not been mentioned: because self-types aren't part of the hierarchy of the required class they can be excluded from pattern matching, especially when you are exhaustively matching against a sealed hierarchy. This is convenient when you want to model orthogonal behaviors such as:
sealed trait Person
trait Student extends Person
trait Teacher extends Person
trait Adult { this : Person => } // orthogonal to its condition
val p : Person = new Student {}
p match {
case s : Student => println("a student")
case t : Teacher => println("a teacher")
} // that's it we're exhaustive
Section 2.3 "Selftype Annotations" of Martin Odersky's original Scala paper Scalable Component Abstractions actually explains the purpose of selftype beyond mixin composition very well: provide an alternative way of associating a class with an abstract type.
The example given in the paper was like the following, and it doesn't seem to have an elegant subclass correspondent:
abstract class Graph {
type Node <: BaseNode;
class BaseNode {
self: Node =>
def connectWith(n: Node): Edge =
new Edge(self, n);
}
class Edge(from: Node, to: Node) {
def source() = from;
def target() = to;
}
}
class LabeledGraph extends Graph {
class Node(label: String) extends BaseNode {
def getLabel: String = label;
def self: Node = this;
}
}
TL;DR summary of the other answers:
Types you extend are exposed to inherited types, but self-types are not
eg: class Cow { this: FourStomachs } allows you to use methods only available to ruminants, such as digestGrass. Traits that extend Cow however will have no such privileges. On the other hand, class Cow extends FourStomachs will expose digestGrass to anyone who extends Cow .
self-types allow cyclical dependencies, extending other types does not
Let's start with the cyclical dependency.
trait A {
selfA: B =>
def fa: Int }
trait B {
selfB: A =>
def fb: String }
However, the modularity of this solution is not as great as it might first appear, because you can override self types as so:
trait A1 extends A {
selfA1: B =>
override def fb = "B's String" }
trait B1 extends B {
selfB1: A =>
override def fa = "A's String" }
val myObj = new A1 with B1
Although, if you override a member of a self type, you lose access to the original member, which can still be accessed through super using inheritance. So what is really gained over using inheritance is:
trait AB {
def fa: String
def fb: String }
trait A1 extends AB
{ override def fa = "A's String" }
trait B1 extends AB
{ override def fb = "B's String" }
val myObj = new A1 with B1
Now I can't claim to understand all the subtleties of the cake pattern, but it strikes me that the main method of enforcing modularity is through composition rather than inheritance or self types.
The inheritance version is shorter, but the main reason I prefer inheritance over self types is that I find it much more tricky to get the initialisation order correct with self types. However, there are some things you can do with self types that you can't do with inheritance. Self types can use a type while inheritance requires a trait or a class as in:
trait Outer
{ type T1 }
trait S1
{ selfS1: Outer#T1 => } //Not possible with inheritance.
You can even do:
trait TypeBuster
{ this: Int with String => }
Although you'll never be able to instantiate it. I don't see any absolute reason for not being be able to inherit from a type, but I certainly feel it would be useful to have path constructor classes and traits as we have type constructor traits / classes. As unfortunately
trait InnerA extends Outer#Inner //Doesn't compile
We have this:
trait Outer
{ trait Inner }
trait OuterA extends Outer
{ trait InnerA extends Inner }
trait OuterB extends Outer
{ trait InnerB extends Inner }
trait OuterFinal extends OuterA with OuterB
{ val myV = new InnerA with InnerB }
Or this:
trait Outer
{ trait Inner }
trait InnerA
{this: Outer#Inner =>}
trait InnerB
{this: Outer#Inner =>}
trait OuterFinal extends Outer
{ val myVal = new InnerA with InnerB with Inner }
One point that should be empathised more is that traits can extends classes. Thanks to David Maclver for pointing this out. Here's an example from my own code:
class ScnBase extends Frame
abstract class ScnVista[GT <: GeomBase[_ <: TypesD]](geomRI: GT) extends ScnBase with DescripHolder[GT] )
{ val geomR = geomRI }
trait EditScn[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]
trait ScnVistaCyl[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]
ScnBase inherits from the Swing Frame class, so it could be used as a self type and then mixed in at the end (at instantiation). However, val geomR needs to be initialised before it's used by inheriting traits. So we need a class to enforce prior initialisation of geomR. The class ScnVista can then be inherited from by multiple orthogonal traits which can themselves be inherited from. Using multiple type parameters (generics) offers an alternative form of modularity.
trait A { def x = 1 }
trait B extends A { override def x = super.x * 5 }
trait C1 extends B { override def x = 2 }
trait C2 extends A { this: B => override def x = 2}
// 1.
println((new C1 with B).x) // 2
println((new C2 with B).x) // 10
// 2.
trait X {
type SomeA <: A
trait Inner1 { this: SomeA => } // compiles ok
trait Inner2 extends SomeA {} // doesn't compile
}
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.
Update: A principal difference is that self-types can depend on multiple classes (I admit that's a bit corner case). For example, you can have
class Person {
//...
def name: String = "...";
}
class Expense {
def cost: Int = 123;
}
trait Employee {
this: Person with Expense =>
// ...
def roomNo: Int;
def officeLabel: String = name + "/" + roomNo;
}
This allows to add the Employee mixin just to anything that is a subclass of Person and Expense. Of course, this is only meaningful if Expense extends Person or vice versa. The point is that using self-types Employee can be independent of the hierarchy of the classes it depends on. It doesn't care of what extends what - If you switch the hierarchy of Expense vs Person, you don't have to modify Employee.
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.