Haskell无法匹配类型,声称刚性变量

时间:2011-06-07 02:09:43

标签: haskell types polymorphism

我是Haskell的新手,我正在为图形及其中的节点创建一个类型类。既然我想要有向图和无向图,我有

data Node = Node { label :: Char
                 , index :: Int
                 } deriving (Ord, Eq)
type Graph edgeType = ([Node], [edgeType])
data Edge = DirectedEdge   {h :: Node, t :: Node}
          | UndirectedEdge {a :: Node, b :: Node}
instance Show Node where
    show n = ['(', label n, ')']
instance Show Edge where
    show (DirectedEdge   h t) = show h ++ "->" ++ show t
    show (UndirectedEdge a b) = show a ++ "-"  ++ show b

所以我区分了有向边和无向边。图表必须只有两种类型的边。我还有以下内容:

nodes :: [Node]
nodes = zipWith Node ['a'..] [0..]

emptyGraph :: [Node] -> Graph edgeType
emptyGraph ns = (ns, [])

到目前为止一直很好,但我正在编写一个函数connect,将节点连接到现有图形。理想情况下,我只希望它适用于无向图,但这似乎不是一种选择。相反,我有这样的事情:

connect :: Graph edgeType -> Node -> Graph edgeType
connect (ns, es) n = (n:ns, e:es)
    where e = UndirectedEdge n (head ns)

但这会产生以下错误:

Couldn't match type `edgeType' with `Edge'
  `edgeType' is a rigid type variable bound by
             the type signature for
               connect :: Graph edgeType -> Node -> Graph edgeType

实现我想要实现的目标的最佳方法是什么?

2 个答案:

答案 0 :(得分:7)

您可能希望使用两种不同的边缘类型而不是Edge

newtype DirectedEdge = DirectedEdge { h :: Node, t :: Node}
newtype UndirectedEdge = UndirectedEdge { a :: Node, b :: Node}

你可能想要一些类型类,它会给你一个(Node, Node)给定一个任意边缘:

class HasNodeEndpoints a where
  endpoints :: a -> (Node, Node)

-- obvious instances for DirectedEdge and UndirectedEdge

然后,当您想谈论任意图表时,您将编写适用于Graph a的函数,可能还会编写HasNodeEndpoints a => Graph a上的函数。对于有向图和无向图,关注图类的算法分别适用于Graph DirectedEdgeGraph UndirectedEdge

另一个自然延伸将被标记为有向和无向边缘。

class HasLabeled a where
  type Label a -- associated type synonym
  label :: a -> Label a
  updateLabel :: a -> (Label a -> Label a) -> a

-- now define data types and instances for labeled directed and undirected edges

答案 1 :(得分:1)

因为您选择特定的边缘类型,即Edge,所以当您使用UndirectedEdge时,结果是您的图形在边缘类型中不再是多态的。它必须具有以下类型:

connect :: Graph Edge -> Node -> Graph Edge
connect (ns, es) n = (n:ns, e:es)
    where e = UndirectedEdge n (head ns)

因为明确使用UndirectedEdge,所以你的边缘可能没有其他类型。


顺便说一句,我会在节点上使用严格注释,这只是为了保持良好的卫生:

data Node = Node { label :: !Char
                 , index :: !Int
                 } deriving (Ord, Eq)