“不符合 trait Builder 的类型参数范围”在 Scala 中是什么意思?
What does 'do not conform to trait Builder's type parameter bounds' mean in scala?
我有以下简单程序,分别为类型参数和抽象类型别名定义了 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 拒绝编译它:
Error:(16, 27) type arguments [I#DD,I#GG] do not conform to trait
Builder's type parameter bounds [D <:
scala.spike.typeBoundInference.Example1.Domain,G <:
scala.spike.typeBoundInference.Example1.StaticGraph[D]]
val impl: StaticGraph.Builder[I#DD, I#GG]
这里可能出了什么问题?
DD <: 域检查
GG <: StaticGraph[DD] 检查
scala 没有理由认为它不安全。
与此同时,我发现如果 class StaticGraph[T] 声明为协变,scala 编译器将 运行 成功。这更糟(出于某种原因 StaticGraph[T] 必须是不变的),因为类型绑定 GG <: StaticGraph[DD] 意味着如果确定类型 DD,则 GG 是 StaticGraph[DD] 的子class ],但不需要 StaticGraph[Domain] 的子class,这正是我在这里想要的。
更新 1:我已经阅读了所有的答案和评论,并以某种方式得到的印象是核心原因是无法保证在任何情况下 i
of Impl
,绑定的类型只保证type
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]
}
}
在这种情况下,编译器会抛出一个错误:
Error:(19, 10) overriding type GG in trait Impl with bounds <:
scala.spike.TypeBoundInference.Example1.StaticGraph[scala.spike.TypeBoundInference.Example1.Impl1.DD];
type GG has incompatible type
type GG = StaticGraph[Domain]
如果您认为类型约束在某些情况下不成立,可以给我反例吗?
UPDATE2:原来根据答案,这是真的:
i.GG <:< StaticGraph[i.DD]
但这可能是错误的:
Impl#GG <:< StaticGraph[Impl#GG]
.
所以在 DSL 的上下文中,这也可能是错误的:
I#GG <:< StaticGraph[I#GG]
(3)
但这只是谜题的一部分,为了证明它是类型不安全的,我们必须构造一个使条件(3)无效的DSL[I]的反例。所以还是老问题:是否可以构造反例?
What could possibly go wrong here?
GG <: StaticGraph[DD] check
通过声明 type GG <: StaticGraph[DD]
,您可以在 成员 类型之间建立关系(与 <: StaticGraph[this.DD]
相同)。这意味着您需要考虑 Impl
.
的实例
对于任何 val i: Impl
,您有 i.DD <: Domain
和 i.GG <: StaticGraph[i.DD]
。你还有 i.DD <: I#DD
。但是你没有 i.DD =:= I#DD
!所以 StaticGraph[i.DD]
和 StaticGraph[I#DD]
不相关(对于不变量 StaticGraph
)。所以 i.GG
(或 I#GG
)和 StaticGraph[I#DD]
.
都不是
要编译通过,需要要求所有i.DD
都相同(这也保证了i.DD =:= I#DD
)。有一种方法可以做到这一点:
trait DSL[T <: Domain, I <: Impl { type DD = T } ]
将使代码编译(无需任何其他更改)。
如果 StaticGraph
是协变的,则关系成立:
I#GG =:= (kind of)
i.GG forSome { val i: I } <:
StaticGraph[i.DD] forSome { val i: I } <:
StaticGraph[I#DD] forSome { val i: I } =:=
StaticGraph[I#DD]
好的问题已解决:
import scala.language.higherKinds
object Example5 {
trait Domain {}
trait D1 extends Domain
trait Impl {
type DD <: Domain
type GG[T <: Domain] <: StaticGraph[T]
}
trait DSL[I <: Impl] {
val impl: Builder[I#DD, I#GG]
}
trait StaticGraph[T <: Domain] {}
trait Builder[D <: Domain, G[T <: Domain] <: StaticGraph[T]] {}
}
我不敢相信我必须为这种平庸的事情使用更高的种类:-<
为什么编译?它解耦了类型约束并将其延迟到有必要时才使用。 (这是我能想到的唯一解释)
我有以下简单程序,分别为类型参数和抽象类型别名定义了 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 拒绝编译它:
Error:(16, 27) type arguments [I#DD,I#GG] do not conform to trait Builder's type parameter bounds [D <: scala.spike.typeBoundInference.Example1.Domain,G <: scala.spike.typeBoundInference.Example1.StaticGraph[D]] val impl: StaticGraph.Builder[I#DD, I#GG]
这里可能出了什么问题?
DD <: 域检查
GG <: StaticGraph[DD] 检查
scala 没有理由认为它不安全。
与此同时,我发现如果 class StaticGraph[T] 声明为协变,scala 编译器将 运行 成功。这更糟(出于某种原因 StaticGraph[T] 必须是不变的),因为类型绑定 GG <: StaticGraph[DD] 意味着如果确定类型 DD,则 GG 是 StaticGraph[DD] 的子class ],但不需要 StaticGraph[Domain] 的子class,这正是我在这里想要的。
更新 1:我已经阅读了所有的答案和评论,并以某种方式得到的印象是核心原因是无法保证在任何情况下 i
of Impl
,绑定的类型只保证type
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]
}
}
在这种情况下,编译器会抛出一个错误:
Error:(19, 10) overriding type GG in trait Impl with bounds <: scala.spike.TypeBoundInference.Example1.StaticGraph[scala.spike.TypeBoundInference.Example1.Impl1.DD]; type GG has incompatible type type GG = StaticGraph[Domain]
如果您认为类型约束在某些情况下不成立,可以给我反例吗?
UPDATE2:原来根据答案,这是真的:
i.GG <:< StaticGraph[i.DD]
但这可能是错误的:
Impl#GG <:< StaticGraph[Impl#GG]
.
所以在 DSL 的上下文中,这也可能是错误的:
I#GG <:< StaticGraph[I#GG]
(3)
但这只是谜题的一部分,为了证明它是类型不安全的,我们必须构造一个使条件(3)无效的DSL[I]的反例。所以还是老问题:是否可以构造反例?
What could possibly go wrong here?
GG <: StaticGraph[DD] check
通过声明 type GG <: StaticGraph[DD]
,您可以在 成员 类型之间建立关系(与 <: StaticGraph[this.DD]
相同)。这意味着您需要考虑 Impl
.
对于任何 val i: Impl
,您有 i.DD <: Domain
和 i.GG <: StaticGraph[i.DD]
。你还有 i.DD <: I#DD
。但是你没有 i.DD =:= I#DD
!所以 StaticGraph[i.DD]
和 StaticGraph[I#DD]
不相关(对于不变量 StaticGraph
)。所以 i.GG
(或 I#GG
)和 StaticGraph[I#DD]
.
要编译通过,需要要求所有i.DD
都相同(这也保证了i.DD =:= I#DD
)。有一种方法可以做到这一点:
trait DSL[T <: Domain, I <: Impl { type DD = T } ]
将使代码编译(无需任何其他更改)。
如果 StaticGraph
是协变的,则关系成立:
I#GG =:= (kind of)
i.GG forSome { val i: I } <:
StaticGraph[i.DD] forSome { val i: I } <:
StaticGraph[I#DD] forSome { val i: I } =:=
StaticGraph[I#DD]
好的问题已解决:
import scala.language.higherKinds
object Example5 {
trait Domain {}
trait D1 extends Domain
trait Impl {
type DD <: Domain
type GG[T <: Domain] <: StaticGraph[T]
}
trait DSL[I <: Impl] {
val impl: Builder[I#DD, I#GG]
}
trait StaticGraph[T <: Domain] {}
trait Builder[D <: Domain, G[T <: Domain] <: StaticGraph[T]] {}
}
我不敢相信我必须为这种平庸的事情使用更高的种类:-<
为什么编译?它解耦了类型约束并将其延迟到有必要时才使用。 (这是我能想到的唯一解释)