У меня есть следующая простая программа, которая определяет 2 одинаковые верхние границы для параметра типа и псевдонима абстрактного типа соответственно:
package scala.spike.typeBoundInference
object Example1 {
trait Domain {
}
trait Impl {
type DD <: Domain
type GG <: StaticGraph[DD]
type A1
type A2
type A3
// ... this type list can go very long
// so inlining them as generic type parameters is impossible
final type Builder = StaticGraph.Builder[DD, GG]
}
trait DSL[I <: Impl] {
val impl: StaticGraph.Builder[I#DD, I#GG]
}
trait StaticGraph[T <: Domain] {}
object StaticGraph {
trait Builder[D <: Domain, G <: StaticGraph[D]] {}
}
}
Однако, scala отказывается его компилировать:
Ошибка: (16, 27) аргументы типа [I # DD, I # GG] не соответствуют признаку
Границы параметра типа строителя [D <:
scala.spike.typeBoundInference.Example1.Domain, G <:
scala.spike.typeBoundInference.Example1.StaticGraph [D]]
val impl: StaticGraph.Builder [I # DD, I # GG] </p>
Что здесь может пойти не так?
нет никаких оснований считать, что это небезопасно.
Тем временем я обнаружил, что если класс StaticGraph [T] объявлен как ковариантный компилятор scala, он будет работать успешно. Это еще хуже (по какой-то причине StaticGraph [T] должен быть инвариантным), так как привязка типа GG <: StaticGraph [DD] означает, что если тип DD определен, то GG является подклассом StaticGraph [DD], но не является необходимым подкласс StaticGraph [Domain], что именно здесь я и хочу. </p>
ОБНОВЛЕНИЕ 1 : я прочитал все ответы и комментарии и каким-то образом у меня сложилось впечатление, что основная причина в том, что нет никакой гарантии, что для любого экземпляра i
из Impl
, тип обязательна только гарантия того типа
i.DD <:< Impl#DD
и Imp#GG <:< StaticGraph[Impl#DD]
но не StaticGraph[i.DD] <:< StaticGraph[Impl#GG]
, таким образом, i.GG <:< StaticGraph[i.DD]
также не гарантируется.
Тем не менее, я провёл быстрый эксперимент, чтобы проверить эту идею, которая оказывается неверной:
object Example1 {
trait Domain {}
class D1 extends Domain {}
trait Impl {
type DD <: Domain
type GG <: StaticGraph[DD]
}
class StaticGraph[T <: Domain] {}
object Impl1 extends Impl {
type DD = D1
type GG = StaticGraph[Domain]
}
//or this:
val impl = new Impl {
type DD = D1
type GG = StaticGraph[Domain]
}
}
В этом случае компилятор выдает ошибку:
Ошибка: (19, 10) переопределение типа GG в признаке Impl с границами <:
scala.spike.TypeBoundInference.Example1.StaticGraph [scala.spike.TypeBoundInference.Example1.Impl1.DD];
тип GG имеет несовместимый тип
тип GG = StaticGraph [домен] </p>
Если вы считаете, что ограничение типа не выполняется в некоторых случаях, не могли бы вы привести контрпример?
UPDATE2 : выясняется, что согласно ответу это правда:
i.GG <:< StaticGraph[i.DD]
но это может быть ложным:
Impl#GG <:< StaticGraph[Impl#GG]
.
, поэтому в контексте DSL это также может быть ложным:
I#GG <:< StaticGraph[I#GG]
(3)
Но это только часть головоломки, чтобы доказать, что это небезопасный тип, мы должны построить контрпример для DSL [I], который делает недействительным условие (3). Таким образом, остается старый вопрос: можно ли построить контрпример?