Last active
July 28, 2016 04:06
-
-
Save Allan-Gong/cbd494b3bef275e7c154b53ca68d7936 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| trait U { | |
| self => | |
| val name = "outer" | |
| val b = new AnyRef { | |
| val name = "inner" | |
| println(name) | |
| println(this.name) | |
| println(self.name) | |
| } | |
| } | |
| // Another example: | |
| trait Foo{ | |
| this: { def close:Unit} => | |
| ... | |
| } | |
| // Here 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. | |
| // Section 2.3 "Selftype Annotations" of Martin Odersky's original Scala paper Scalable Component Abstractions explains the purpose of selftype beyond mixin composition very well: | |
| // provide an alternative way of associating a class with an abstract type: | |
| abstract class Graph { | |
| type Node <: BaseNode; // The abstract Node type is upper-bounded by BaseNode to express that we want nodes to support a connnectWith method | |
| class BaseNode { | |
| self: Node => | |
| def connectWith(n: Node): Edge = | |
| // This method creates a new instnce of class Edge which links the receiver node with the argument node. | |
| new Edge(self, n); // illegal!! Because the type of the self reference this is BaseNode and therefore does not conform to type Node which is expected by the constructor of class Edge. | |
| def self: Node; // Thus, we have to state somehow that the identity of class BaseNode has to be expressible as type Node. | |
| // Node is called the selftype of class BaseNode. When a selftype is given, it is taken as the type of this inside the class. | |
| // Without a selftype annotation, the type of this is taken as usual to the type of the class itself | |
| } | |
| 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; | |
| // Concreate subclasses of Graph have to define a concrete Node class for which it is | |
| // possible to implement method self | |
| def self: Node = this; | |
| } | |
| } | |
| // This programming pattern appears quite frequently when family polymorphism is combined with explict reference to this. | |
| // Therefore, Scala supports a mechanism for specifying the type of this explicitly. | |
| // The selftype of a class must be a subtype of the selftypes of all its base classes. | |
| // When instantiating a class in a new expression, it is checked that the selftype of the class is a supertype of the type of the object being created. | |
| // Here is another example to demostrate the difference between a self type and extending a trait. | |
| // If you say B extends A, then B is an A. | |
| // However, when you do dependency injection, you want B to require A, not to be an A. | |
| // For example: | |
| trait User { def name:String } | |
| trait Tweeter { | |
| user: User => | |
| def tweet(msg:String) = println(s"$name: $msg") | |
| trait Wrong extends Tweeter { | |
| // Error: illegal inheritance. | |
| // self-type Wrong does not conform to Tweeter's selftype Tweeter with User | |
| def noCanDo = name // error: not found: value name | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment