繁体   English   中英

Haskell类型上下文来自函数所需的实例声明

[英]Haskell Type Context from instance declaration required on functions

我在为我编写的数据类型执行实例声明时使用了类型上下文。

data Set a = Insert a (Set a) | EmptySet

instance (Show a) => Show (Set a) where
    show x = "{" ++ show' x ++ "}" where
        show' (Insert x EmptySet) = show x
        show' (Insert x xs) = show x ++ ", " ++ show' xs

instance Eq a => Eq (Set a) where
    (Insert x xs) == (Insert y ys) = (x == y) && (xs == ys)

所以现在,我必须将Eq类型上下文添加到我定义的所有使用我的Set类型的函数中,就像这样,或者我得到一个类型错误:

memberSet::Eq a =>a->Set a->Bool
memberSet _ EmptySet = False
memberSet x (Insert y ys)
    | x == y = True
    | otherwise = memberSet x ys

subSet::Eq a=>Set a->Set a->Bool
subSet EmptySet _ = True
subSet (Insert a as) bs
    | memberSet a bs = subSet as bs
    | otherwise = False

我得到的错误看起来像:

    No instance for (Eq a)
      arising from a use of `=='
    In the expression: (x == y)
    In a stmt of a pattern guard for
                 an equation for `memberSet':
        (x == y)
    In an equation for `memberSet':
        memberSet x (Insert y ys)
          | (x == y) = True
          | otherwise = memberSet x ys
Failed, modules loaded: none.

这甚至意味着什么? 为什么我会收到此错误? 我想,一旦我做了实例声明,Haskell就能自动验证在我的函数“memberSet”和“subSet”中被“==”比较的东西会自动被检查为“Eq?”的实例。

为清晰起见编辑:

我的问题是我不明白为什么在“memberSet”和“subSet”上需要类型上下文。 如果我这样删除它们,它就不会编译。

  memberSet::a->Set a->Bool
    memberSet _ EmptySet = False
    memberSet x (Insert y ys)
        | x == y = True
        | otherwise = memberSet x ys

    subSet::Set a->Set a->Bool
    subSet EmptySet _ = True
    subSet (Insert a as) bs
        | memberSet a bs = subSet as bs
        | otherwise = False

只是为了它的乐趣,你可以安排它,以便通过使用GADT在函数上不需要约束:

{-# LANGUAGE GADTs #-}
module Set where

data Set x where
    EmptySet :: Set a
    Insert :: Eq a => a -> Set a -> Set a

instance Show a => Show (Set a) where
    show EmptySet = "{}"
    show xs = "{" ++ show' xs ++ "}"
      where
        show' (Insert a EmptySet) = show a
        show' (Insert a ys) = show a ++ ", " ++ show' ys

instance Eq (Set a) where
    (Insert x xs) == (Insert y ys) = x == y && xs == ys
    EmptySet == EmptySet = True
    _ == _ = False

memberSet :: a -> Set a -> Bool
memberSet x (Insert y ys) = x == y || memberSet x ys
memberSet _ _ = False

subSet :: Set a -> Set a -> Bool
subSet EmptySet _ = True
subSet (Insert a as) bs
    | memberSet a bs = subSet as bs
    | otherwise = False

通过将Eq约束放在类型的Insert构造函数上,我们可以确保

  1. 对于不在Eq类型,不能构造非空集。
  2. 每当我们在Insert构造函数上进行模式匹配时, Eq上下文(和字典)都可用,因此我们不需要在函数的类型签名中提及它。

你的实例声明说的是, Set a是实例Eq每当a是。 事实上, a是否是Eq一个实例完全是另一回事; 这只允许你将两个Set==进行比较,而在memberSet你只是比较元素。

考虑类型Integer -> Integer 这不是Eq的实例,原因应该是显而易见的。 如果Set包含该类型的元素,您期望memberSet如何工作?

我想知道你在这里希望完成的是确保只能创建一个元素类型为Eq实例的Set 如果是这样,这是一个非常不同的问题,但也大多是不必要的 - 使用Set s将Eq约束留在函数上最终用于相同的目的。

为什么不看一下标准的Data.Set模块呢? 请注意,在集合上运行的大多数函数都具有Ord约束,这反映了所使用的内部表示是二叉搜索树的事实。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM