在动态图上使用图函数的Haskell FGL
我的目标是使用形状的相交图进行操作。相交图有节点:R^n中的形状,如果它们相交,则节点之间有一条边 在Haskell中,实现一个函数在动态图上使用图函数的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
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报告中指定的类型不同,这就是为什么在GHCishow.read
中没有给出类型错误。)
哦,我差点忘了DynGraph
/Graph
。如果您查看Haddock或源代码,您将看到DynGraph
被定义为class-Graph-gr=>DynGraph-gr,其中…
。因此,每个动态图
也是一个图
;类型签名f::DynGraph g=>…
允许您在类型g
上同时使用DynGraph
和Graph
函数。换句话说,这不是你遇到的问题。问题是编译器无法推断类型gr0
(未声明的中间类型)是任一类的成员
编辑:更准确地说,实际使用类函数需要类约束或已知为类成员的特定类型。我们的函数f
和f'
是单态的;所有类型都已指定,GHC应该能够生成实际的可运行代码。但请记住,类函数是按类型定义的;GHC可以从哪里获得f
中的类函数(这里的术语是“类字典”)
指定类型(如myf'
)是一种解决方案。您还可以使用依赖项注入样式使函数本身具有多态性。您至少需要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)