2011-07-30 66 views
5

背景信息:我目前正试图建立一个通用图库,其中包括几种不同的搜索算法(我已经开始使用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

你知道这里有一个图库,不是吗? –

+0

因为我这样做是为了爱好,而且因为几分钟的四处搜索并没有导致明确的答案,所以我想我可能会把自己放在一起。 – Dylan

+0

我很好奇你什么条件,你完全谷歌?我试过'scala图库',并得到了这个:http://code.google.com/p/scala-graphs/对你来说不是这样吗? – AndreasScheinert

回答

6

Daniel可能是对的,现有的Scala类型推理器需要更多的直接信息来找出那个E必须是Edge。另外,我的理解是类型推断故意被指定为未来的改进。

无论如何,我认为你可以采取另一种方法来解决类型推断问题:使用类型成员而不是参数。我已经用下面的自包含代码说明了我的意思。关键想法是类型EV成为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)) 
} 

编辑一个这种类型的部件的方法的潜在的限制是在方法Dijkstra该穿类型参数G较少的约束。具体而言,边界WeightedGraphOpsDirectedGraphOps不限于具有相同类型的成员E。我不知道如何解决这个问题,而不会碰到你最初报告的类型推断问题。一种方法是在这个问题中的模式:Why do these type arguments not conform to a type refinement?,但似乎Scala编译器无法处理它。

编辑2忽略上述段落。正如迪伦在评论中提到的,对于这种diamond inheritance的情况,斯卡拉很好地确保了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) 
    } 
} 
+0

这帮了我,谢谢!我不确定是否应该担心边缘类型约束的缺失 - 我可能错过了某些内容,但我无法创建一个混合了Ops类型但具有不同'E'类型的对象。它甚至有可能吗? – Dylan

+0

有趣。这听起来像钻石问题(http://en.wikipedia.org/wiki/Diamond_problem)的类型。对于数值,Scala对钻石问题的解决方案是基于线性化方案的一个值覆盖另一个值。对于类型,我不惊讶这是不允许的,以避免不一致。 –

2

为什么应该是Edge?如果您查看Dijkstra的声明,您会看到没有参数提及E(graph:G, start:V)。所以斯卡拉有一个线索G应该是什么,以及V应该是什么。没有参考E的参数。

+0

但是不能有足够聪明的Scala编译器在此调用站点找出它吗?知道'graph'具有类型'WeightedGraphOps [Position,Edge] ...'基本上修复了'E'必须使类型工作。 –

+0

它应该是'Edge',因为这是GraphMap中边缘的类型。 Dijkstra's的实施需要这种类型,所以我不能把它排除在外。 – Dylan

+0

@Dylan我明白你有一个问题,我只是想让你更好地理解类型推断约束,所以你会更容易编写代码。这里的问题是:类型是从参数中推断出来的。 'G'是从'graph'推断出来的。什么是参数'E'应该推断出来?它不会从类型约束中推断出来 - 这正是它所检查的内容,从它推断出的内容。 –