I want to to extend a trait within a trait, like this:
trait NodeTypes {
trait Node {
def allNodesHaveThis: Int
}
}
trait ScrumptiousTypes e
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 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).
§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).
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
!
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