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

后端 未结 3 2131
忘掉有多难
忘掉有多难 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 05:38

    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
      }
    
    }
    
    0 讨论(0)
  • 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!

    0 讨论(0)
  • 2021-02-19 06:03

    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 
    
    0 讨论(0)
提交回复
热议问题