I have been reading about Dotty, since it looks like it is about to become scala 3, and noticed that type projections are deemed "unsound" and removed from the language ...
This seems like a bummer, as I have seen several use cases where they were really useful. For example:
trait Contents
class Foo extends Contents
class Bar extends Contents
trait Container[T <: Contents] { type ContentType = T }
class FooContainer extends Container[Foo]
class BarContainer extends Container[Bar]
trait Manager[T <: Container[_]] {
type ContainerType = T
type ContentType = T#ContentType
def getContents: ContentType
def createContainer(contents: ContentType): ContainerType
}
How would one do something like this in Dotty? Add a second type parameter to Manager
? But, aside from the fact that it makes it really tedious to create and manipulate instances of the Manager
, it also doesn't quite work, as there is no way to enforce the relationship between the two types (Manager[FooContainer, Bar]
should not be legal).
Then, there are other uses, like type lambdas, and partially applied types, that are useful for creating biased functors etc ... Or do these (partially applied types) become "first class citizens" in Dotty?
EDIT
To answer the question in the comments, here is a somewhat contrived example of his this may be used. Let's suppose, my Managers
are actually Akka Actors
:
abstract class BaseManager[T <: Container[_]](
val storage: ContentStorage[T#ContentType]
) extends Actor with Manager[T] {
def withContents(container: T, content: ContentType): ContainerType
def withoutContents: T
var container: T = withoutContents
def receive: Receive {
case ContentsChanged =>
container = withContents(container, storage.get)
case ContainerRequester =>
sender ! container
// ... other common actions
}
}
class FooManager(storage: FooStorage) extends BaseManager[FooContainer](storage) {
def withContents(container: FooContainer, content: Foo) =
container.copy(Some(content))
def withoutContent = FooContainer(None)
override def receive: Receive = super.receive orElse {
// some additional actions, specific to Foo
}
}
case class FooContainer(content: Option[Foo]) extends Container[Foo]{
// some extremely expensive calculations that happen when
// content is assigned, so that we can cache the result in container
}
See Question&Answers more detail:
os 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…