scala - scala 中的“不符合 trait Builder 的类型参数界限”是什么意思?
问题描述
我有以下简单的程序,分别为类型参数和抽象类型别名定义了 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] 不符合 trait Builder 的类型参数边界 [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 没有理由认为它不安全。
同时,我发现如果类 StaticGraph[T] 被声明为协变 scala 编译器将成功运行。这更糟糕(由于某种原因 StaticGraph[T] 必须是不变的),因为类型绑定 GG <: StaticGraph[DD] 意味着如果类型 DD 被确定,那么 GG 是 StaticGraph[DD] 的子类,但不是必需的StaticGraph[Domain] 的子类,这正是我想要的。
更新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) 在 trait Impl 中覆盖类型 GG,边界 <: scala.spike.TypeBoundInference.Example1.StaticGraph[scala.spike.TypeBoundInference.Example1.Impl1.DD]; type GG 具有不兼容的类型 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] 的反例。所以老问题仍然存在:是否有可能构建一个反例?
解决方案
这里可能出了什么问题?
GG <: StaticGraph[DD] 检查
通过声明您在成员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]
推荐阅读
- arduino - 到 Raspberry Pi(Rasbian)的 Arduino USB 串行连接未初始化
- git - 提交新代码时如何修复错误“未找到 husky-run”?
- deployment - gh-pages 部署问题,部署时作业失败。您尝试部署的目录...不存在
- bash - 在 bash 脚本中获取文件尾部的值并存储为变量
- django - TypeError: connect() 参数 4 必须是 str,而不是 WindowsPath。/*我在简单的登录 django 项目中遇到的错误*/
- python - 如何从 Google Drive API 下载电子表格
- python - Python - 单个图中的多个图 - 在不同列中循环
- c - 如何测试 C 预处理器 -D 标志值?
- serilog - 例如,如果信号在过去 24 小时内没有事件,有没有办法生成错误事件?
- laravel - 将索引传递给方法 reduce() laravel