Haskell FGL在DynGraph上使用Graph函数

时间:2014-10-02 19:20:48

标签: haskell graph typechecking

我的目标是用形状的交叉图做事。交叉图具有节点:R ^ n中的形状,如果它们相交,则节点之间存在边缘。

在Haskell中,实现了一个函数,

makeIntersectionGraph :: DynGraph g => [Shape] -> g Shape ()
makeIntersectionGraph sh = ... begin with the empty graph and add nodes and edges as you walk
                       ... through all possible intersections

以上编译和类型检查很好。一个简单的事情应该是使用labNodes函数获取图形的节点。

getNodes sh = labNodes (makeIntersectionGraph sh)

请注意,文档说明:

  

DynGraph:动态,可扩展的图形。动态图表继承静态图形中的所有操作,但也提供扩展和更改图形的操作。

labNodesGraph类的函数。所以上面应该有效。

然而,我收到错误:

No instance for (Graph gr0) arising from a use of `labNodes'
    In the expression: labNodes (makeIntersectionGraph es)
    In an equation for `makeIntersectionGraphComplete':
        makeIntersectionGraphComplete es
          = labNodes (makeIntersectionGraph es)

DFN2Graph.hs:346:46:
    No instance for (DynGraph gr0)
      arising from a use of `makeIntersectionGraph'
    In the first argument of `labNodes', namely
      `(makeIntersectionGraph es)'
    In the expression: labNodes (makeIntersectionGraph es)
    In an equation for `makeIntersectionGraphComplete':
        makeIntersectionGraphComplete es
          = labNodes (makeIntersectionGraph es)

----更新---- 我找到了解决方案,但我不明白问题是什么。如果我改变了类型

makeIntersectionGraph :: DynGraph g => [Shape] -> g Shape ()

makeIntersectionGraph ::  [Shape] -> G.Gr Shape ()

,其中

import qualified Data.Graph.Inductive.PatriciaTree as G

然后

getNodes sh = labNodes (makeIntersectionGraph sh)

完美无缺。我仍然遇到的问题是,当所有DynGraph都可以访问相同的函数时,我不明白为什么有必要调用DynGraph的具体实现。

1 个答案:

答案 0 :(得分:3)

您正在使用两个函数:一个创建图形,另一个使用图形。我们现在简化类型并忽略DynGraph / Graph区别。

makeGraph :: Graph g => [Shape] -> g Shape ()
makeGraph = undefined

consumeGraph :: Graph g => g Shape () -> [LNode Shape]
consumeGraph = undefined

f :: [Shape] -> [LNode Shape]
f = consumeGraph . makeGraph -- same as f x = consumeGraph (makeGraph x)

编辑:将其拆分出来:

f :: [Shape] -> [LNode Shape] -- Note: no g!
f shapes = consumeGraph g
  where g :: Graph g => g -- This annotation is just illustrative
        g = makeGraph shapes

问题是g没有出现在f的类型签名中。编译器可以想象选择满足限制的几种类型中的任何一种,但这是任意的,它不会这样做。

所有Graph共享相同功能的事实并没有真正回答这个问题。中间图是否会使用Patricia树或普通树?它有所作为。想象一下,如果我们讨论类似Num的类:我们可以使用IntDoubleIntegerDecimal,每个都有完全不同的性能和语义特征。

所以需要在某个地方指出你想要的类型。您的解决方案有效如果你想保持makeIntersectionGraph的一般性,你可以使用内部类型注释,沿着这些方向:

f' :: [Shape] -> [LNode Shape]
f' xs = consumeGraph (makeGraph xs :: G.Gr Shape)

这个问题经常出现。我认为它是“show . read”问题;这是类似的情况。我们在中间使用什么类型? The Haskell '98 report has a section on "ambiguous types",当您尝试使用Num函数执行此操作时,这也解释了默认规则导致此问题屏蔽的原因。

(请注意,在GHCi中,来自'98报告中指定的type defaulting rules are changed,这就是GHCi show . read中的不会出现类型错误的原因。)< / p>

哦,我几乎忘记了DynGraph / Graph。如果您查看Haddock或来源,您会看到DynGraph被定义为class Graph gr => DynGraph gr where ...。因此,每个DynGraph也是Graph;类型签名f :: DynGraph g => ...允许您在DynGraph类型上使用Graphg函数。换句话说,这不是你遇到的问题。问题是编译器无法推断类型gr0(未说明的中间类型)是 类的成员。

编辑:更准确地说,实际上使用类函数需要一个类约束或一个已知属于该类成员的特定类型。我们的函数ff'单态;指定了所有类型,GHC应该能够生成实际的,可运行的代码。但请记住,每个类型都定义了类函数; GHC去哪里获取f中的类函数(这里的术语是“类字典”)?

指定类型,就像我的f'一样,是一种解决方案。您还可以使用一种依赖注入样式使函数本身 polymorphic 。您至少需要ScopedTypeVariables

f'' :: Graph g => proxy g -> [Shape] -> [LNode Shape]
f'' _ shapes = consumeGraph (makeGraph shapes :: g)

请注意,函数的第一个参数不需要实际存在;它只是一个呼叫者指定g类型的机制。来电者将使用Proxy中的Data.Proxy。在this SO question以及此处和其他地方的许多其他地方对Proxy进行了一些讨论。