0

我有一个特点,要求Graphlike作为图表工作的东西。值得注意的是,我想要的属性之一是该方法g.subgraph(Set(1, 2, 3))将返回一个仅具有顶点 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 理解这两种关联类型在所有派生对象中都是相同的?

4

2 回答 2

2

最简单的解决方案似乎是这个。只要确保子图返回相同的类型,this.type就可以了。没有必要A——它只是增加了额外的复杂性,因为你试图证明它Athis.

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))
}

链接到 Scastie:https ://scastie.scala-lang.org/RolPc3ggTxeZ2tUqdXKNEQ


如果您subgraph这样定义,它也可以工作:

def subgraph(selectedVertices: Set[_ >: Vertex]): this.type

因为它是逆变的,即使Vertex在不同的类和/或特征中不同,它也会起作用。

链接到 Scastie:https ://scastie.scala-lang.org/fz509HEpTBGoJGaJxLziBQ

于 2020-05-20T15:34:41.043 回答
2

val aut: Automaton with Graphlike[Automaton]首先,自Automaton extends Graphlike[Automaton](so Automaton with Graphlike[Automaton] =:= Automaton)以来,写作没有任何意义。

其次,我猜你想Graphlike在. 所以你应该添加到.VertexStateAutomatonoverride type Vertex = StateAutomaton

第三,Vertex是路径依赖型。aut.Vertexa.Vertex(其中val a: Automaton = aut.subgraph(Set(aut.initialState))) 是不同的类型。如果你想subgraph接受Set[x.Vertex]不同的,x: Automaton那么你应该使用类型投影修改它的签名

def subgraph(selectedVertices: Set[A#Vertex]): A

(整个代码:https ://scastie.scala-lang.org/pKfCrEjDToOXi0e7fDEt7w )

修改签名的另一种方法subgraph是(如@user建议的那样)

def subgraph(selectedVertices: Set[Vertex]): this.type

因此,您应该扩展参数类型(从Set[Vertex]aka Set[this.Vertex]to Set[A#Vertex])或缩小返回类型(from Ato this.type)。

于 2020-05-20T16:01:17.857 回答