Considering the following example:
trait Supe {
type Out <: Supe
def out: Out
}
class Reif1 extends Supe {
type Out = Reif1
override def out: Out = this
}
class Reif2 extends Supe {
type Out >: this.type <: Reif2
override def out: Out = this
}
class Reif1
should obviously work. as type Out
is reified and becomes a type alias
class Reif2
also works but seriously? type Out
only has upper/lower bound defined and the bound was not even tight enough: this.type
is a singleton type, and Reif2
is a class type. So what exactly would Out
look like if Reif2
got instantiated? Is it going to be this.type
? Or Reif2
? But the bigger question should be: why scalac 2.12/2.13 allows it be compiled?
In Scala (or in DOT calculus 1 2) all types are intervals.
type Out = Reif1
is (or is supposed to be) type Out >: Reif1 <: Reif1
.
Abstract type without bounds type Out
is type Out >: Nothing <: Any
.
So what exactly would
Out
look like ifReif2
got instantiated?
It will remain exactly type Out >: this.type <: Reif2
val r = new Reif2
import scala.reflect.runtime.universe._
typeOf[r.Out] // App.r.Out
showRaw(typeOf[r.Out]) // TypeRef(SingleType(ThisType(App), TermName("r")), TypeName("Out"), List())
typeOf[r.Out].typeSymbol.isAbstract // true
typeOf[r.Out].typeSymbol.typeSignature // >: Reif2.this.type <: App.Reif2
If you replace type Out >: this.type <: Reif2
in Reif2
with type Out = this.type
(or type Out = Reif2
or type Out = Supe
) then isAbstract
will return false
.
Abstract type member of a singleton object (see application of abstract type)
What is the meaning of a type declaration without definition in an object? (see why it's not easy to check that type T >: L <: U
is not abstract)
Concrete classes can have abstract type members #1753
SI-8217 allow abstract type members in objects #4024
Abstract type members are incorrectly forbidden in objects (unless inherited) #8217
Use of abstract type in a concrete class?
Concrete classes with abstract type members
What's different between "def apply[T](c:T)" and "type T;def apply(c:T)"