Skip to content

Instantly share code, notes, and snippets.

@Allan-Gong
Last active July 28, 2016 04:06
Show Gist options
  • Save Allan-Gong/cbd494b3bef275e7c154b53ca68d7936 to your computer and use it in GitHub Desktop.
Save Allan-Gong/cbd494b3bef275e7c154b53ca68d7936 to your computer and use it in GitHub Desktop.
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