如何在 Scala 中将 F 有界多态与关联类型结合起来?
How to combine F-bounded polymorphism with associated types in Scala?
我有一个叫做 Graphlike
的特性,它可以作为图表使用。值得注意的是,我想要的属性之一是方法 g.subgraph(Set(1, 2, 3))
会 return 一个相同类型的子图,只有顶点 1、2 和 3。显然,这意味着我想要 F-有界多态性 Graphlike
看起来像这样:
trait Graphlike[A <: Graphlike[A]] {
type Vertex
def subgraph(selectedVertices: Set[Vertex]): A
}
我还有一个特征,它代表一个自动机,具有关联的边和顶点类型。我希望它表现得像一个图表。简化后,它看起来像这样:
trait Automaton extends Graphlike[Automaton] {
type State
type Vertex = State
def states: Iterable[State]
def initialState: State
}
这几乎行得通。但是,当我尝试将两者混合并对结果做一些有用的事情时,Scala 的类型系统变得混乱:
class UsesAutomataAsGraphs(val aut: Automaton with Graphlike[Automaton]) {
aut.subgraph(Set(aut.initialState)).subgraph(Set(aut.initialState))
}
给出如下错误:
[info] Compiling 1 Scala source to /Users/albin/Downloads/example/target/scala-2.12/classes ...
[error] /Users/albin/Downloads/example/src/main/scala/example/Example.scala:21:56: type mismatch;
[error] found : UsesAutomataAsGraphs.this.aut.State
[error] required: _1.State where val _1: Automaton
[error] aut.subgraph(Set(aut.initialState)).subgraph(Set(aut.initialState))
如何让 Scala 理解这两个关联类型在所有派生对象中都是相同的?
最简单的解决方案似乎是这个。只需确保子图 returns 与 this.type
的类型相同即可。不需要 A
- 它只会增加额外的复杂性,因为您试图证明 A
是 this
.
的类型
trait Graphlike {
type Vertex
def subgraph(selectedVertices: Set[Vertex]): this.type
}
trait Automaton extends Graphlike {
type State
type Vertex = State
def states: Iterable[State]
def initialState: State
}
class UsesAutomataAsGraphs(val aut: Automaton) {
aut.subgraph(Set(aut.initialState)).subgraph(Set(aut.initialState))
}
在斯卡斯蒂:https://scastie.scala-lang.org/zMtde7VISKi18LdPXO6Ytw
让 State
成为类型参数也对我有用。请注意,在 UsesAutomataAsGraphs
中,如果您使用 A <: Automaton[_]
(通配符),它不起作用,因为 State
可以是任何东西。编译器希望你保证返回的 Automaton
将具有相同的 State
类型(因为它是无界的,其他 类 扩展 Automaton
可以不同地定义它)。
trait Graphlike[A <: Graphlike[A]] {
type Vertex
def subgraph(selectedVertices: Set[Vertex]): A
}
trait Automaton[State] extends Graphlike[Automaton[State]] {
type Vertex = State
def states: Iterable[State]
def initialState: State
}
class UsesAutomataAsGraphs[S](val aut: Automaton[S]) {
aut.subgraph(Set(aut.initialState)).subgraph(Set(aut.initialState))
}
Link 给斯卡斯蒂:https://scastie.scala-lang.org/RolPc3ggTxeZ2tUqdXKNEQ
如果您这样定义 subgraph
,它也有效:
def subgraph(selectedVertices: Set[_ >: Vertex]): this.type
因为它是逆变的,即使 Vertex
在不同的 类 and/or 特征中不同,它也会起作用。
Link 给斯卡斯蒂:https://scastie.scala-lang.org/fz509HEpTBGoJGaJxLziBQ
首先,写 val aut: Automaton with Graphlike[Automaton]
是没有意义的,因为 Automaton extends Graphlike[Automaton]
(所以 Automaton with Graphlike[Automaton] =:= Automaton
)。
其次,我猜你希望 Graphlike
的 Vertex
成为 Automaton
中的 State
。所以你应该添加 override type Vertex = State
到 Automaton
.
第三,Vertex
是路径依赖类型。 aut.Vertex
和 a.Vertex
(其中 val a: Automaton = aut.subgraph(Set(aut.initialState))
)是不同的类型。如果你想让 subgraph
为不同的 x: Automaton
接受 Set[x.Vertex]
那么你应该使用类型 projection
修改它的签名
def subgraph(selectedVertices: Set[A#Vertex]): A
(整个代码:https://scastie.scala-lang.org/pKfCrEjDToOXi0e7fDEt7w)
另一种修改subgraph
签名的方法是(如@user所建议)
def subgraph(selectedVertices: Set[Vertex]): this.type
因此您应该扩展参数类型(从 Set[Vertex]
又名 Set[this.Vertex]
到 Set[A#Vertex]
)或缩小 return 类型(从 A
到 this.type
).
我有一个叫做 Graphlike
的特性,它可以作为图表使用。值得注意的是,我想要的属性之一是方法 g.subgraph(Set(1, 2, 3))
会 return 一个相同类型的子图,只有顶点 1、2 和 3。显然,这意味着我想要 F-有界多态性 Graphlike
看起来像这样:
trait Graphlike[A <: Graphlike[A]] {
type Vertex
def subgraph(selectedVertices: Set[Vertex]): A
}
我还有一个特征,它代表一个自动机,具有关联的边和顶点类型。我希望它表现得像一个图表。简化后,它看起来像这样:
trait Automaton extends Graphlike[Automaton] {
type State
type Vertex = State
def states: Iterable[State]
def initialState: State
}
这几乎行得通。但是,当我尝试将两者混合并对结果做一些有用的事情时,Scala 的类型系统变得混乱:
class UsesAutomataAsGraphs(val aut: Automaton with Graphlike[Automaton]) {
aut.subgraph(Set(aut.initialState)).subgraph(Set(aut.initialState))
}
给出如下错误:
[info] Compiling 1 Scala source to /Users/albin/Downloads/example/target/scala-2.12/classes ...
[error] /Users/albin/Downloads/example/src/main/scala/example/Example.scala:21:56: type mismatch;
[error] found : UsesAutomataAsGraphs.this.aut.State
[error] required: _1.State where val _1: Automaton
[error] aut.subgraph(Set(aut.initialState)).subgraph(Set(aut.initialState))
如何让 Scala 理解这两个关联类型在所有派生对象中都是相同的?
最简单的解决方案似乎是这个。只需确保子图 returns 与 this.type
的类型相同即可。不需要 A
- 它只会增加额外的复杂性,因为您试图证明 A
是 this
.
trait Graphlike {
type Vertex
def subgraph(selectedVertices: Set[Vertex]): this.type
}
trait Automaton extends Graphlike {
type State
type Vertex = State
def states: Iterable[State]
def initialState: State
}
class UsesAutomataAsGraphs(val aut: Automaton) {
aut.subgraph(Set(aut.initialState)).subgraph(Set(aut.initialState))
}
在斯卡斯蒂:https://scastie.scala-lang.org/zMtde7VISKi18LdPXO6Ytw
让 State
成为类型参数也对我有用。请注意,在 UsesAutomataAsGraphs
中,如果您使用 A <: Automaton[_]
(通配符),它不起作用,因为 State
可以是任何东西。编译器希望你保证返回的 Automaton
将具有相同的 State
类型(因为它是无界的,其他 类 扩展 Automaton
可以不同地定义它)。
trait Graphlike[A <: Graphlike[A]] {
type Vertex
def subgraph(selectedVertices: Set[Vertex]): A
}
trait Automaton[State] extends Graphlike[Automaton[State]] {
type Vertex = State
def states: Iterable[State]
def initialState: State
}
class UsesAutomataAsGraphs[S](val aut: Automaton[S]) {
aut.subgraph(Set(aut.initialState)).subgraph(Set(aut.initialState))
}
Link 给斯卡斯蒂:https://scastie.scala-lang.org/RolPc3ggTxeZ2tUqdXKNEQ
如果您这样定义 subgraph
,它也有效:
def subgraph(selectedVertices: Set[_ >: Vertex]): this.type
因为它是逆变的,即使 Vertex
在不同的 类 and/or 特征中不同,它也会起作用。
Link 给斯卡斯蒂:https://scastie.scala-lang.org/fz509HEpTBGoJGaJxLziBQ
首先,写 val aut: Automaton with Graphlike[Automaton]
是没有意义的,因为 Automaton extends Graphlike[Automaton]
(所以 Automaton with Graphlike[Automaton] =:= Automaton
)。
其次,我猜你希望 Graphlike
的 Vertex
成为 Automaton
中的 State
。所以你应该添加 override type Vertex = State
到 Automaton
.
第三,Vertex
是路径依赖类型。 aut.Vertex
和 a.Vertex
(其中 val a: Automaton = aut.subgraph(Set(aut.initialState))
)是不同的类型。如果你想让 subgraph
为不同的 x: Automaton
接受 Set[x.Vertex]
那么你应该使用类型 projection
def subgraph(selectedVertices: Set[A#Vertex]): A
(整个代码:https://scastie.scala-lang.org/pKfCrEjDToOXi0e7fDEt7w)
另一种修改subgraph
签名的方法是(如@user所建议)
def subgraph(selectedVertices: Set[Vertex]): this.type
因此您应该扩展参数类型(从 Set[Vertex]
又名 Set[this.Vertex]
到 Set[A#Vertex]
)或缩小 return 类型(从 A
到 this.type
).