在动态图上使用图函数的Haskell FGL

在动态图上使用图函数的Haskell FGL,haskell,graph,typechecking,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

我的目标是使用形状的相交图进行操作。相交图有节点: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)
请注意,文件说明:

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

labNodes
Graph
类的函数。因此,上述方法应该有效

但是,我得到了一个错误:

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

在哪里

然后


很好用。我仍然存在的问题是,我不明白为什么在所有DynGraph都可以访问相同的函数时调用DynGraph的具体实现是必要的。

您使用的是两个函数:一个用于生成图形,另一个用于使用图形。现在让我们简化类型并忽略
DynGraph
/
图形
的区别

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
s共享相同的函数这一事实并不能真正回答这个问题。中间图将使用Patricia树还是普通树?这很重要。想象一下,如果我们谈论一个类,比如
Num
:我们可以使用
Int
Double
Integer
Decimal
,每个类都具有完全不同的性能和语义特征

因此,需要有一些指示,在某个地方,你想要哪种类型。你的解决方案有效;如果要保持
makeIntersectionGraph
的通用性,可以使用内部类型注释,如下所示:

f' :: [Shape] -> [LNode Shape]
f' xs = consumeGraph (makeGraph xs :: G.Gr Shape)
这个问题经常出现。我认为这是一个“
show.read
”问题;这是一个类似的情况。我们在中间使用什么类型?这也解释了为什么在尝试使用
Num
函数时,默认规则会掩盖此问题

(请注意,在GHCi中,与'98报告中指定的类型不同,这就是为什么在GHCi
show.read
中没有给出类型错误。)

哦,我差点忘了
DynGraph
/
Graph
。如果您查看Haddock或源代码,您将看到
DynGraph
被定义为
class-Graph-gr=>DynGraph-gr,其中…
。因此,每个
动态图
也是一个
;类型签名
f::DynGraph g=>…
允许您在类型
g
上同时使用
DynGraph
Graph
函数。换句话说,这不是你遇到的问题。问题是编译器无法推断类型
gr0
(未声明的中间类型)是任一类的成员

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

指定类型(如my
f'
)是一种解决方案。您还可以使用依赖项注入样式使函数本身具有多态性。您至少需要
ScopedTypeVariables

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

注意,函数的第一个参数不需要实际存在;它只是调用方指定
g
类型的一种机制。调用方将使用
Data.Proxy
中的
Proxy
。这里和其他地方都有一些关于代理的讨论。

关于ghc 7.6.3,(show.read)/does/给出了编译时错误。所以我猜问题是f:(图g)=>(A->g)和h:(图g)=>g->B不能真正组合。我把它读为(A->B)和(B->C),然后是(A->C),这应该是可行的。B实际上是Gr g=>g。所以我能说问题是Haskell生成了Gr g1和Gr g2,并且没有办法将gr1和gr2统一起来吗?有没有办法在类型系统中强制实现这种平等?没有,您的
f
h
应该是可组合的。此外,我相信GHC确实“统一”了您的
gr1
gr2
。这并不是说平等缺失了。这是因为GHC不能为
Graph
(即
Graph
类实例中为某些特定类型定义的所有函数)发明类字典,而不具有实际类型或类型变量--把他们从我这里拉出来。
f :: [Shape] -> [LNode Shape] -- Note: no g!
f shapes = consumeGraph g
  where g :: Graph g => g -- This annotation is just illustrative
        g = makeGraph shapes
f' :: [Shape] -> [LNode Shape]
f' xs = consumeGraph (makeGraph xs :: G.Gr Shape)
f'' :: Graph g => proxy g -> [Shape] -> [LNode Shape]
f'' _ shapes = consumeGraph (makeGraph shapes :: g)