具有多个参数的Haskell和TypeClass

具有多个参数的Haskell和TypeClass,haskell,Haskell,在编写相互继承的类型类时,我遇到了一个简单的问题。我试图创建类型类的层次结构,以实现某种程度的表示抽象。假设我想要一个集合类型类: {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} class Collection c a where isMember :: a -> c -> Bool 我定义了一种树类型: data Tree a = Empty | Node a (Tree

在编写相互继承的类型类时,我遇到了一个简单的问题。我试图创建类型类的层次结构,以实现某种程度的表示抽象。假设我想要一个集合类型类:

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}

class Collection c a where
  isMember :: a -> c -> Bool
我定义了一种树类型:

data Tree a = Empty | Node a (Tree a) (Tree a)
  deriving (Show, Eq)
我想让我的树成为一个收藏,因此:

inOrder :: Tree a -> [a]
inOrder Empty = []
inOrder (Node a l r) = (inOrder l) ++ [a] ++ (inOrder r)

instance (Eq a) => Collection (Tree a) a where
  isMember a c = a `elem` (inOrder c)
这不太合适:

*Main> (isMember '2' Empty)
<interactive>:1:1:
    No instance for (Collection (Tree a) Char)
      arising from a use of `isMember' at <interactive>:1:1-18
    Possible fix:
      add an instance declaration for (Collection (Tree a) Char)
    In the expression: (isMember '2' Empty)
    In the definition of `it': it = (isMember '2' Empty)
*Main>(isMember'2'为空)
:1:1:
(集合(树a)字符)没有实例
因在1:1-18使用“isMember”而产生
可能的解决方案:
为(集合(树a)字符)添加实例声明
在表达式中:(isMember'2'为空)
在“it”的定义中:it=(isMember“2”为空)

如果我必须为每个具体类型创建一个实现,那么typeclass的值可能会丢失。所以我没有正确地编写实例声明。但是我不太清楚如何继续。

您需要添加一个函数依赖项,以指示容器类型决定元素类型:

{-# LANGUAGE FunctionalDependencies #-}

class Collection c a | c -> a where
    ...

还有其他方法可以做到这一点,比如使用类型族,但我认为这是这种情况下最简单的解决方法。

问题是
Empty
的类型是
树a
,ghc没有足够的信息来知道在这种情况下希望
a
字符。如果手动指定类型,则它将起作用:

isMember '2' (Empty::Tree Char)

ghc不能仅仅推断
a
必须是
Char
的原因是理论上可以有一个实例
Collection(Tree Char)Char
和另一个
Collection(Tree Int)Char
。在这种情况下,除非您明确指定,否则无法推断应该使用哪一个。

这里的问题是,默认情况下,每个类型类参数都独立于其他参数。简单地将
isMember
应用于
Char
和未知元素类型的树不足以让它推断应该使用(看似明显的)实例

这显然不是你想要的工作方式,启动有点傻,但这就是它的工作方式。为了解决这个问题,您需要为它提供某种连接方式,对于这种连接还有其他扩展:
FunctionalDependencies
是更常见的,而
TypeFamilies
是更新的,更易于使用,但仍然有一些粗糙的边缘

对于函数依赖关系,您可以指定类型类参数的某些子集完全决定其他参数,例如
集合c a | c->a
。这有其自身的缺陷,但在许多情况下效果足够好

对于类型族,您可能会将其简化为单个参数类型类,并关联元素类型,例如:

class Collection c where
    type Elem c
    isMember :: Elem c -> c -> Bool