无法在haskell中匹配具有特定类型的类类型

时间:2016-10-31 18:25:37

标签: haskell typeclass

我的代码中存在以下情况(简化但语义相同)

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)

有人能告诉我我做错了吗?

2 个答案:

答案 0 :(得分:9)

你误解了类方法的类型; edges :: EdgeSet c => a -> c是一个函数,它将任何 a(受a限制为Graph的实例)并返回任何 c(约束为cEdgeSet c的实例。你可能想要说它返回某些 c服从上述约束。

Haskell 98解决方案

你可以要求边缘为所有图形返回实际的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