我的代码中存在以下情况(简化但语义相同)
class Graph a where
edges :: EdgeSet c => a -> c
我有许多子类型可以满足图形界面。其中一个是树
data Tree = Tree
instance Graph Tree where
edges tree = DirectedEdgeSet
对edge方法的调用应返回DirectedEdgeSet
。这应该没问题,因为DirectedEdgeSet
实现了EdgeSet
类:
type Edge = (Int, Int)
data DirectedEdgeSet = DirectedEdgeSet (Set Edge) Int
class EdgeSet c where
content :: c -> Set Edge
size :: c -> Int
instance EdgeSet DirectedEdgeSet where
content (DirectedEdgeSet es _) = es
size (DirectedEdgeSet _ x) = x
此示例无法编译:
• Couldn't match expected type ‘c’
with actual type ‘DirectedEdgeSet’
‘c’ is a rigid type variable bound by
the type signature for:
edges :: forall c. EdgeSet c => Tree -> c
at Tree.hs:10:5
• In the expression: DirectedEdgeSet
In an equation for ‘edges’: edges tree = DirectedEdgeSet
In the instance declaration for ‘Graph Tree’
• Relevant bindings include
edges :: Tree -> c (bound at Tree.hs:10:5)
有人能告诉我我做错了吗?
答案 0 :(得分:9)
你误解了类方法的类型; edges :: EdgeSet c => a -> c
是一个函数,它将任何 a
(受a
限制为Graph
的实例)并返回任何 c
(约束为c
是EdgeSet c
的实例。你可能想要说它返回某些 c
服从上述约束。
你可以要求边缘为所有图形返回实际的Set
(如Data.Set
中的那个):
class Graph a where
edges :: a -> Data.Set Edge
ExistentialQuantification
解决方案否则,您可以使用ExistentialQuantification
扩展名并修改类方法:
{-# LANGUAGE ExistentialQuantification #-}
data SomeEdgeSet = forall c. EdgeSet c => SomeEdgeSet c
class Graph a where
edges :: a -> SomeEdgeSet
instance Graph Tree where
edges tree = SomeEdgeSet DirectedEdgeSet
正如您所知,Graph
的每个实例都在使用SomeEdgeSet
时返回edges
,但SomeEdgeSet
包含任何内容,只要该内容是EdgeSet
的实例。
TypeFamilies
解决方案这是我推荐的解决方案。一般来说,对于任何Graph
,您只会返回一个类型的Edges
。然后,有一个很酷的功能TypeFamilies
,你可以在类中声明一个类型:
{-# LANGUAGE TypeFamilies, UndecideableInstances #-}
class (EdgeSet (Edges a)) => Graph a where
type Edges a
edges :: a -> Edges a
然后,假设您对Graph Tree
的边缘的表示为DirectedEdgeSet
,您的实例将如下所示:
class Graph Tree where
type Edges Tree = DirectedEdgeSet -- `DirectedEdgeSet` is the type here
edges tree = DirectedEdgeSet -- `DirectedEdgeSet` is the constructor here
答案 1 :(得分:5)
在edges
中,类型变量c
是普遍量化的。这意味着edges
必须适用于作为EdgeSet
实例的所有类型。您的实现修复了具体类型,因此不适用于所有 EdgeSet
实例。
要解决此问题,您可以通过类型系列指定实例使用的边集的具体类型:
{-# LANGUAGE TypeFamilies #-}
data Tree = Tree
data DirectedEdgeSet = DirectedEdgeSet
class Graph a where
type GraphEdgeSet a :: *
edges :: a -> GraphEdgeSet a
instance Graph Tree where
type GraphEdgeSet Tree = DirectedEdgeSet
edges tree = DirectedEdgeSet