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.
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
In your case, if you change the order from
ScrumptiousTypes with YummyTypes
toYummyTypes with ScrumptiousTypes
, then error will be on methodyumminess
.An alternate to @余杰水 is to extend inner class like,
use type also fix the issue
------v2------- use object contain to Node , but since path depend it is not a good idea , and maybe It will be problems
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
The big question is: What does the spec say that
super.Node
insideYummyTypes
refers to? To find out, we'll need to know the definitions of the spec-specific terms used above:So, a template is what we'd ordinarily call an object, class, or trait definition.
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 beGraph
,YummyTypes
,ScrumptiousTypes
,NodeTypes
, followed by standard stuff likeAny
. Indeed, this is confirmed when I modifyGraph
like this:which produces:
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
inGraph
should beScrumptiousTypes
. And so, the actual supertype ofYummyTypes.Node
inGraph
should beScrumptiousTypes.Node
.However, adding this line to
Graph
:produces:
ScrumptiousTypes.Node
is missing. Apparently, insideYummyTypes
,super.Node
does not refer toNode
inYummyTypes
' actual least proper supertype.However, if I add:
printing
text
inGraph
produces:demonstrating that inside
YummyTypes
inGraph
,super.text
does refer toScrumptiousTypes.text
!