Class 在haskell中定义多类型容器类,绑定变量时遇到问题

Class 在haskell中定义多类型容器类,绑定变量时遇到问题,class,haskell,functional-programming,Class,Haskell,Functional Programming,我在哈斯克尔的课上遇到了麻烦 基本上,我有一个算法(一种奇怪的图形遍历算法),它将一个容器作为输入,用来存储已经看到的节点(我热衷于避免单子,所以让我们继续:)。问题是,该函数将容器作为参数,只调用一个函数:“set_contains”,它询问容器是否。。。包含节点v。(如果您好奇的话,另一个作为参数传入的函数会执行实际的节点添加) 基本上,我想尝试各种数据结构作为参数。然而,由于没有重载,我不能让一个以上的数据结构使用最重要的contains函数 所以,我想做一个“设置”类(我知道我不应该自己

我在哈斯克尔的课上遇到了麻烦

基本上,我有一个算法(一种奇怪的图形遍历算法),它将一个容器作为输入,用来存储已经看到的节点(我热衷于避免单子,所以让我们继续:)。问题是,该函数将容器作为参数,只调用一个函数:“set_contains”,它询问容器是否。。。包含节点v。(如果您好奇的话,另一个作为参数传入的函数会执行实际的节点添加)

基本上,我想尝试各种数据结构作为参数。然而,由于没有重载,我不能让一个以上的数据结构使用最重要的contains函数

所以,我想做一个“设置”类(我知道我不应该自己做)。多亏了Chris Okasaki的书,我已经建立了一个非常漂亮的红黑树,现在剩下的就是创建set类并声明RBT,以及其他一些实例

以下是代码:

(注意:代码大量更新——例如,contains现在不调用帮助函数,而是类函数本身!)

数据颜色=红色|黑色
数据(Ord a)=>RBT a=树叶|树木颜色(RBT a)a(RBT a)
实例显示颜色在哪里
显示红色=“r”
显示黑色=“b”
类集合t在哪里
包含::(Ord a)=>t->a->Bool
--我知道这是毫无意义的,只是表明它可以编译。
实例(Ord a)=>等式(RBT a),其中
叶=叶=真
(Tree uux ux)==(Tree uuy)=x==y
实例(Ord a)=>集合(RBT a),其中
包含叶b=False
包含t@(树c l x r)b
|b==x=True
|b
注意,我有一个非常愚蠢的RBT Eq实例。这是故意的——我是从别人那里抄来的(但抄近路)

基本上,我的问题可以归结为:如果我注释掉Set的实例化语句(rbta),那么一切都可以编译。如果我将其重新添加,则会出现以下错误:

RBTree.hs:21:15:
    Couldn't match expected type `a' against inferred type `a1'
      `a' is a rigid type variable bound by
          the type signature for `contains' at RBTree.hs:11:21
      `a1' is a rigid type variable bound by
           the instance declaration at RBTree.hs:18:14
    In the second argument of `(==)', namely `x'
    In a pattern guard for
       the definition of `contains':
          b == x
    In the definition of `contains':
        contains (t@(Tree c l x r)) b
                   | b == x = True
                   | b < x = contains l b
                   | otherwise = contains r b
RBTree.hs:21:15:
无法将预期的类型“a”与推断的类型“a1”匹配
`a'是一个刚性类型变量,由
RBTree中“contains”的类型签名。hs:11:21
`a1'是一个刚性类型变量,由
RBTree上的实例声明。hs:18:14
在“(==)”的第二个参数中,即“x”
以防
“包含”的定义:
b==x
在“包含”的定义中:
包含(t@(树c l x r))b
|b==x=True
|b
就我的一生而言,我根本无法弄明白为什么这不起作用。(作为旁注,“contains”函数在别处定义,基本上具有RBT数据类型的实际set_contains逻辑。)

谢谢阿戈尔


第三次编辑:删除以前的编辑,合并在上面。

错误意味着类型不匹配。
包含的
类型是什么?(如果它的类型不是像
t->a->Bool
那样的
set\u所包含的
is,那么就有问题了。)

为什么你认为不应该滚动自己的类

Set(RBT a)
编写实例时,只为特定类型
a
定义contains。也就是说,
RBT Int
是一组
Int
s,
RBT Bool
是一组
Bools
,等等

但是您对
Set t
的定义要求
t
同时是所有有序
a
的集合

也就是说,鉴于
包含的类型,这应该进行类型检查:

tree :: RBT Bool
tree = ...

foo = contains tree 1
显然不会

有三种解决方案:

  • 使
    t
    成为类型构造函数变量:

    类集合t在哪里 包含::(Ord a)=>TA->a->Bool

    实例集RBT在哪里 ...

  • 这适用于
    RBT
    ,但不适用于许多其他情况(例如,您可能希望将位集用作
    Int
    s的集合)

  • 功能依赖性:

    类(Ord a)=>设置TA | t->a其中 包含::t->a->Bool

    实例(Ord a)=>集合(RBT a)a,其中

  • 有关详细信息,请参阅

  • 相关类型:

    类集合t在哪里 类型元素t::* 包含::t->元素t->Bool

    实例(Ord a)=>集合(RBT a),其中 类型元素(RBT a)=a

  • 有关详细信息,请参见。

    您需要一个多参数类型类。您当前对
    集合t
    的定义没有在类定义中提及包含的类型,因此成员
    包含的
    必须适用于任何
    a
    。请尝试以下操作:

    class Set t a | t -> a where
        contains :: (Ord a) => t-> a-> Bool
    
    
    instance (Ord a) => Set (RBT a) a where
        contains Leaf b = False
        contains t@(Tree c l x r) b
            | b == x    = True
            | b < x     = contains l b
            | otherwise = contains r b
    

    为了扩展Ganesh的答案,您可以使用类型族而不是函数依赖项。我认为它们更好。而且它们对代码的更改也更少

    {-# LANGUAGE FlexibleContexts, TypeFamilies #-}
    
    class Set t where
      type Elem t
      contains :: (Ord (Elem t)) => t -> Elem t -> Bool
    
    instance (Ord a) => Set (RBT a) where
      type Elem (RBT a) = a
      contains Leaf b = False
      contains (Tree c l x r) b
        | b == x    = True
        | b < x     = contains l b
        | otherwise = contains r b
    
    {-#语言灵活上下文,类型族}
    类集合t在哪里
    类型元素t
    包含::(Ord(要素t))=>t->要素t->Bool
    实例(Ord a)=>集合(RBT a),其中
    类型元素(RBT a)=a
    包含叶b=False
    包含(树c l x r)b
    |b==x=True
    |b
    您也可以使用。您的类的定义方式需要一个具有*的类型t。您可能想要的是您的集合类采用一个容器类型,就像您的RBT具有kind*->*

    通过将
    t
    应用于类型变量,您可以轻松修改类以赋予类型t一种*->*,如下所示:

    class Set t where
        contains :: (Ord a) => t a -> a -> Bool
    
    然后修改实例声明以删除类型变量
    a

    instance Set RBT where
        contains Leaf b = False
        contains t@(Tree c l x r) b
            | b == x    = True
            | b < x     = contains l b
            | otherwise = contains r b
    
    实例集RBT,其中
    包含叶b=False
    包含t@(树c l x r)b
    |b==x=True
    |b
    下面是使用sma修改的完整代码
    class Set t where
        contains :: (Ord a) => t a -> a -> Bool
    
    instance Set RBT where
        contains Leaf b = False
        contains t@(Tree c l x r) b
            | b == x    = True
            | b < x     = contains l b
            | otherwise = contains r b
    
    data Color = Red | Black
    data (Ord a) => RBT a = Leaf | Tree Color (RBT a) a (RBT a)
    
    instance Show Color where
        show Red = "r"
        show Black = "b"
    
    class Set t where
        contains :: (Ord a) => t a -> a -> Bool
    
    -- I know this is nonesense, just showing it can compile.
    instance (Ord a) => Eq (RBT a) where
        Leaf == Leaf = True
        (Tree _ _ x _) == (Tree _ _ y _) = x == y
    
    instance Set RBT where
        contains Leaf b = False
        contains t@(Tree c l x r) b
            | b == x    = True
            | b < x     = contains l b
            | otherwise = contains r b
    
    tree = Tree Black (Tree Red Leaf 3 Leaf) 5 (Tree Red Leaf 8 (Tree Black Leaf 12 Leaf))
    
    main =
        putStrLn ("tree contains 3: " ++ test1) >>
        putStrLn ("tree contains 12: " ++ test2) >>
        putStrLn ("tree contains 7: " ++ test3)
        where test1 = f 3
              test2 = f 12
              test3 = f 7
              f = show . contains tree
    
    tree contains 3: True tree contains 12: True tree contains 7: False