When extending a trait within a trait, what does 'super' refer to?

后端 未结 3 2129
忘掉有多难
忘掉有多难 2021-02-19 05:34

I want to to extend a trait within a trait, like this:

  trait NodeTypes {
    trait Node {
      def allNodesHaveThis: Int
    }
  }

  trait ScrumptiousTypes e         


        
3条回答
  •  梦谈多话
    2021-02-19 06:01

    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!

提交回复
热议问题