I'm learning about F-bound types in Scala, and I have come across a situation where I don't know what's wrong.
I've made three tests, the code is as follows:
import scala.collection.mutable
def test1() = {
trait Abstract {
type ThisType <: Abstract
def deepCopy(): ThisType
}
case class Concrete1(a: Int) extends Abstract {
override type ThisType = Concrete1
override def deepCopy(): ThisType = this.copy()
}
case class Concrete2(a: Int) extends Abstract {
override type ThisType = Concrete2
override def deepCopy(): ThisType = this.copy()
}
val set = new mutable.HashSet[Abstract]()
set ++= List(Concrete1(1), Concrete2(2))
val set2: mutable.Set[Abstract] = set.map(_.deepCopy())
}
def test2() = {
trait Abstract {
type ThisType
def deepCopy(): ThisType
}
case class Concrete1(a: Int) extends Abstract {
override type ThisType = Concrete1
override def deepCopy(): ThisType = this.copy()
}
case class Concrete2(a: Int) extends Abstract {
override type ThisType = Concrete2
override def deepCopy(): ThisType = this.copy()
}
val set = new mutable.HashSet[Abstract]()
set ++= List(Concrete1(1), Concrete2(2))
val set2: mutable.Set[Abstract] = set.map(_.deepCopy())
}
def test3() = {
trait Abstract[T <: Abstract[T]] {
def deepCopy(): T
}
case class Concrete1(a: Int) extends Abstract[Concrete1] {
override def deepCopy(): Concrete1 = this.copy()
}
case class Concrete2(a: Int) extends Abstract[Concrete2] {
override def deepCopy(): Concrete2 = this.copy()
}
val set = new mutable.HashSet[Abstract[_]]()
set ++= List(Concrete1(1), Concrete2(2))
val set2: mutable.Set[Abstract[_]] = set.map(_.deepCopy())
}
test1 works fine. test2 and test3 produce a compile-time error.
In test2, I ommit that ThisType
is a subtype of Abstract
. I understand that if I don't set this upper bound, ThisType
can be anything. But if I have a Set
of Abstract
and I perform a deepCopy()
of it's elements, wouldn't it be of the same type? The compiler produces this error:
Error:(53, 45) type mismatch;
found : scala.collection.mutable.HashSet[Abstract#ThisType]
required: scala.collection.mutable.Set[Abstract]
val set2: mutable.Set[Abstract] = set.map(_.deepCopy())
^
I don't understand why in this case (test2) Abstract#ThisType
is not the same type as Abstract
and in test1 is. Does it have something to do with path-dependent types? If so, what is the explanation?
In test3 I try to do de same as in test1 but with type parameters, this complier throws an error at line val set2: mutable.Set[Abstract[_]] = set.map(_.deepCopy())
saying:
Error:(78, 48) type mismatch;
found : scala.collection.mutable.HashSet[Any]
required: scala.collection.mutable.Set[Abstract[_]]
Note: Any >: Abstract[_], but trait Set is invariant in type A.
You may wish to investigate a wildcard type such as `_ >: Abstract[_]`. (SLS 3.2.10)
val set2: mutable.Set[Abstract[_]] = set.map(_.deepCopy())
^
Error:(140, 45) type mismatch;
found : scala.collection.mutable.HashSet[Abstract#ThisType]
required: scala.collection.mutable.Set[Abstract]
val set2: mutable.Set[Abstract] = set.map(_.deepCopy())
^
Error:(166, 48) type mismatch;
found : scala.collection.mutable.HashSet[Any]
required: scala.collection.mutable.Set[Abstract[_]]
Note: Any >: Abstract[_], but trait Set is invariant in type A.
You may wish to investigate a wildcard type such as `_ >: Abstract[_]`. (SLS 3.2.10)
val set2: mutable.Set[Abstract[_]] = set.map(_.deepCopy())
^
It's something related to wildcards, but I don't know how to declare such types without wildcards.
def deepCopy(): Abstract[T]
. No other changes needed, but it should be noted that without the "self type" (self :T =>
) you're not getting the safety guarantees of F-bounded polymorphism. – jwvhclass Concrete2(a: Int) extends Abstract[Concrete1] {..
, which usually isn't what you want. – jwvh