背景信息:我目前正在尝试设置一个通用图库,其中包含一些不同的搜索算法(我已经开始使用Dijkstra)。我已经设置了一些特征来表示在某些类型的图表中可以找到的方法(例如加权,定向):
trait GraphOps[V,E] { ... }
trait WeightedGraphOps[V,E] extends GraphOps[V,E] { ... }
trait DirectedGraphOps[V,E] extends GraphOps[V,E] { ... }
object GraphOps{
def Dijkstra[V,E,G <: WeightedGraphOps[V,E] with DirectedGraphOps[V,E]](graph:G, start:V) = { ... }
}
在其他地方,我有一个类作为加权有向图的具体实现,我想运行Dijkstra算法:
class GraphMap[T](...)
extends scala.collection.mutable.Map[Position,T]
with WeightedGraphOps[Position,Edge] with DirectedGraphOps[Position,Edge] { ... }
但是当我试图测试它时:
val graph = new GraphMap[Int](...)
val (dist, prev) = GraphOps.Dijkstra(graph, Position(0,0))
问题:编译期间出现以下错误:error: inferred type arguments [com.dylan.data.Position,Nothing,com.dylan.data.GraphMap[Int]] do not conform to method Dijkstra's type parameter bounds [V,E,G <: com.dylan.data.WeightedGraphOps[V,E] with com.dylan.data.DirectedGraphOps[V,E]]
我花了很长时间才注意到它将我的Edge(E
)类型推断为Nothing
,但我不明白为什么它无法成功推断出它应该是Edge
。为什么无法推断出该类型参数,我该如何解决?
P.S。我尝试了以下操作,并让它工作,但这对于本来应该是一种方便的方法来说似乎非常不方便:
type Helpful = WeightedGraphOps[Position,Edge] with DirectedGraphOps[Position,Edge]
val (dist, prev) = GraphOps.Dijkstra[Position,Edge,Helpful](graph, Position(0,0))
答案 0 :(得分:6)
Daniel可能是正确的,现有的Scala类型推断器需要更多直接信息来确定E
必须Edge
。此外,据我了解,类型推断有意不足以为未来的改进让路。
无论如何,我认为您可以采用另一种解决类型推断问题的设计方法:使用类型成员而不是参数。我已经用下面的自包含代码说明了我的意思。关键的想法是类型E
和V
成为GraphOps
类型的一部分,但它们仍然可以通过使用类型细化作为类型参数浮出水面,与Dijkstra
方法一样。
trait GraphOps { type E; type V }
trait WeightedGraphOps extends GraphOps { }
trait DirectedGraphOps extends GraphOps { }
object GraphOps{
def Dijkstra[V0, G <: (WeightedGraphOps{type V = V0})
with (DirectedGraphOps{type V = V0})]
(graph:G, start:V0) = { }
}
case class Position(x: Int, y: Int)
case class Edge()
case class GraphMap[T]() extends WeightedGraphOps with DirectedGraphOps {
type E = Edge
type V = Position
}
object Test {
val graph = new GraphMap[Int]( )
GraphOps.Dijkstra(graph, Position(0,0))
}
编辑:此类型成员方法的一个潜在限制是对方法G
中的类型参数Dijkstra
设置较少的约束。具体而言,边界WeightedGraphOps
和DirectedGraphOps
不限于具有相同类型的成员E
。我不知道如何解决这个问题,而不会遇到您最初报告的类型推断问题。一种方法是这个问题中的模式:Why do these type arguments not conform to a type refinement?,但似乎Scala编译器无法处理它。 德尔>
Edit2 忽略上述段落。正如Dylan在评论中提到的,对于这种diamond inheritance情况,Scala很好地确保了类型E
的一致性。例如,以下编译正常:
trait GraphOps { type E; type V }
trait WeightedGraphOps extends GraphOps { def f(e: E) }
trait DirectedGraphOps extends GraphOps { def e: E }
object GraphOps{
def Dijkstra[V0, G <: (WeightedGraphOps{type V = V0}) with (DirectedGraphOps{type V = V0})] (graph:G, start:V0) = {
graph.f(graph.e)
}
}
答案 1 :(得分:2)
为什么它应该是Edge
?如果您查看Dijkstra
的声明,您会发现所有参数都没有引用E
:(graph:G, start:V)
。所以Scala有一个线索是G
应该是什么,以及V
应该是什么。参考E
没有参数。