向Haskell中实例声明的上下文添加类型约束

向Haskell中实例声明的上下文添加类型约束,haskell,functional-programming,instance,typeclass,Haskell,Functional Programming,Instance,Typeclass,我试图表示加权边。我最终希望OutE成为Eq和Ord的一个实例,但约束条件是etype是Eq和Ord的一个实例。假设我有以下文件作为temp.hs: data (Ord etype)=> OutE vtype etype = OutE {destVertex:: vtype, edgeValue::etype} applyFunBy accessor ordfun = (\x y -> (ordfun (accessor x) (accessor y))) instance Eq

我试图表示加权边。我最终希望OutE成为Eq和Ord的一个实例,但约束条件是etype是Eq和Ord的一个实例。假设我有以下文件作为temp.hs:

data (Ord etype)=> OutE vtype etype = OutE {destVertex:: vtype, edgeValue::etype}

applyFunBy accessor ordfun = (\x y -> (ordfun (accessor x) (accessor y)))

instance Eq (OutE vtype etype) where
    --(==) :: Ord etype => (OutE vtype etype) -> (OutE vtype etype) -> Bool
    --(/=) :: Ord etype => (OutE vtype etype) -> (OutE vtype etype) -> Bool
    (==) = applyFunBy edgeValue (==)
    (/=) = applyFunBy edgeValue (/=)
在ghci中加载此文件时,会出现以下错误:

temp.hs:10:19:
    Could not deduce (Ord etype)
      from the context (Eq (OutE vtype etype))
      arising from a use of `edgeValue' at temp.hs:10:19-27
    Possible fix:
      add (Ord etype) to the context of the instance declaration
    In the first argument of `applyFunBy', namely `edgeValue'
    In the expression: applyFunBy edgeValue (==)
    In the definition of `==': == = applyFunBy edgeValue (==)

temp.hs:11:19:
    Could not deduce (Ord etype)
      from the context (Eq (OutE vtype etype))
      arising from a use of `edgeValue' at temp.hs:11:19-27
    Possible fix:
      add (Ord etype) to the context of the instance declaration
    In the first argument of `applyFunBy', namely `edgeValue'
    In the expression: applyFunBy edgeValue (/=)
    In the definition of `/=': /= = applyFunBy edgeValue (/=)
Failed, modules loaded: none.
如果包括(==)和(\=)的类型签名行,我得到:

第一个问题:这是一种糟糕的风格。数据类型声明不应该有约束。将约束留给函数,就像containers包那样

instance Eq (OutE vtype etype) where
第二个“问题”。您可以在数据声明之后添加
派生(Eq)
。我猜您知道这一点,并且正在为自己的学习明确地编写实例(对您有好处)

第三个问题:如果权益属于
Eq
类别,则无法对其进行比较。所以你想说
etype
受等式约束:

instance (Eq etype) => Eq (OutE vtype etype) where
    (==) = applyFunBy edgeValue (==)
    (/=) = applyFunBy edgeValue (/=)

第四,实际上不需要为(==)和(/=)都编写实例。一旦您定义了其中一个,默认值将起作用。

您在定义
OutE
时将
etype
限制为
Ord

data (Ord etype) => OutE vtype etype = ...
但是在
Eq
实例中,实际上您正试图不受限制地为任何
etype
定义实例

instance Eq (OutE vtype etype) where
当然,这不起作用,因为
OutE
本身只是为
Ord-etype
s定义的,因此您还必须将typeclass约束添加到实例定义中

instance (Ord etype) => Eq (OutE vtype etype) where
请注意,
=
/=
的一个定义足以让typeclass工作


注意,在
数据
-类型上不设置typeclass约束,而仅在实际需要typeclass功能的实例/方法上设置,这通常更容易,因此被认为是更好的样式

在许多情况下,不需要约束,只会得到不必要的笨拙类型签名

例如,一些有序映射类型
Ord key=>map key value

如果我们只想列出所有的键呢?或者获取元素的数量?对于这些,我们不需要将键设置为
Ord
,因此,为什么不使用simple让map不受限制呢

getKeys :: Map key value -> [key]
getLength :: Map key value -> Int
当我们在函数中真正需要它时,只需添加typeclass,如

insert :: Ord key => key -> value -> Map key value

派生(Eq)
将基于所有记录字段生成相等运算符(从而生成一个带有
Eq vtype
的笨重的
Eq
实例),而问题中给出的显式实例只是基于
edgeValue
进行比较。是的,直到写下该位之后,我才注意到他正在这样做。谢谢你指出这一点。
instance (Ord etype) => Eq (OutE vtype etype) where
getKeys :: Map key value -> [key]
getLength :: Map key value -> Int
insert :: Ord key => key -> value -> Map key value