Cannot override a type with non-volatile upper bound

后端 未结 3 1792
一个人的身影
一个人的身影 2020-12-31 13:30


I have a compiler error in scala and I don\'t know what does it refer to:
Assume these declarations:

trait Abstract {
  type MyType
}
trait AInner
trait         


        
3条回答
  •  执念已碎
    2020-12-31 14:04

    You can rewrite trait B with (more later about your goal, which, I think, is a bit different)

    trait B extends A {
      type MyType <: BInner with AInner
    }
    

    And this makes total sense. A value of type B#MyType can be seen as either a BInner or a AInner.

    You don't need to repeat Abstract because A is already a subclass of Abstract. You don't have to write override as this is implicit for a type declaration. So the question is why A#MyType is not working as AInner?

    Here is what the scala language spec says about volatile types.

    3.6 Volatile Types

    Type volatility approximates the possibility that a type parameter or abstract type instance of a type does not have any non-null values. As explained in (§3.1), a value member of a volatile type cannot appear in a path. A type is volatile if it falls into one of four categories: A compound type T1 with ... with Tn {R } is volatile if one of the following two conditions hold. 1. One of T2, ..., Tn is a type parameter or abstract type, or 2. T1 is an abstract type and and either the refinement R or a type Tj for j > 1 contributes an abstract member to the compound type, or 3. one of T1, ..., Tn is a singleton type. Here, a type S contributes an abstract member to a type T if S contains an abstract member that is also a member of T . A refinement R contributes an abstract member to a type T if R contains an abstract declaration which is also a member of T . A type designator is volatile if it is an alias of a volatile type, or if it designates a type parameter or abstract type that has a volatile type as its upper bound. A singleton type p.type is volatile, if the underlying type of path p is volatile. An existential type T forSome {Q } is volatile if T is volatile.

    Other important item mentioned by the spec is about abstract type overriding:

    Another restriction applies to abstract type members: An abstract type member with a volatile type (§3.6) as its upper bound may not override an abstract type member which does not have a volatile upper bound.

    The compiler error is:

    error: overriding type MyType in trait A with bounds <: AInner;
    type MyType is a volatile type; cannot override a type with non-volatile upper bound
    

    This is consistent with the spec. BInner with A#MyType is volatile. Before that MyType had a non-volatile as Any.

    The matter is that a type in the scala type system must have a unique meaning. An abstract type can be thought as a type which declaration is deferred to a subclass. Therefore there is no problem for declaring values of an abstract type when it is still abstract. On the other hand if we have a type like BInner with A#MyType, this type may have several meaning. It is called volatile and it does not makes sense to have a non null value of this type, as it could have as many types as subclasses instantiating the MyType abstract type. To simplify things, we could think of a volatile type as a type not being a subtype of Any (and volatile as being a subtype Any). We therefore have a contradiction that the compiler mentions.

    Coming back to your goal, which you stated as

    What I'm trying to achieve here(in trait B) is to further restrict the type MyType declared > in Abstract, so any value of type MyType must extend all the MyTypes in the mixin tree.

    You can achieve this thanks to inner traits like this.

    trait Abstract {
      type MyType
    }
    trait B extends Abstract {
      trait MyType {
        def bMethod : Int
      }
    }
    trait A extends B {
      trait MyType extends super.MyType {
      }
    }
    

    Well I hope this somewhat what you're looking for.

提交回复
热议问题