我遇到了定义序数类型的问题,这些序数类型的值可能是也可能不是序数。
基本上我有两种类型,OrderedType
和UnorderedType
data OrderedType = One | Two | Three deriving (Eq, Ord, Show)
data UnorderedType = Red | Blue | Green deriving (Eq, Show)
我有一个类型,其值构造函数将其中一个作为参数:
data WrapperType = WrappedOne OrderedType
| WrappedTwo UnorderedType deriving (Eq, Ord, Show)
基本上,我想做的是订购WrapperType
,而无需
必须为compare
和WrappedOne
实现WrappedTwo
函数。
当我尝试编译以上内容时,出现以下错误:
• No instance for (Ord UnorderedType)
arising from the first field of ‘WrappedTwo’ (type ‘UnorderedType’)
Possible fix:
use a standalone 'deriving instance' declaration,
so you can specify the instance context yourself
• When deriving the instance for (Ord WrappedType)
这是有道理的,因为Ord
的股票衍生WrappedType
实例将尝试
比较WrappedTwo
的所有值。
简而言之,我想做的是这样:
WrappedOne _ < WrappedTwo _ -- True
但是不必为每种类型编写一个Ord
实例。
我该怎么做?
答案 0 :(得分:3)
不清楚您想要什么;我想您想将所有用WrappedTwo
构造的值都视为等效值吗?
newtype ForgetOrder a = ForgetOrder a
instance Eq (ForgetOrder a) where
_ == _ = True
instance Ord (ForgetOrder a) where
compare _ _ = EQ
然后您可以将类型定义为:
data WrapperType = WrappedOne OrderedType
| WrappedTwo (ForgetOrder UnorderedType) deriving (Eq, Ord, Show)
是的,必须包装和拆开新类型有点麻烦,并且编写模式同义词来避免它也很麻烦。 C'est la vie。
但是,我担心您也想要WrappedTwo Red /= WrappedTwo Green
。在这一点上,我将不得不与Wagner先生一起赶上潮流,并说,无论您走过的思路是什么,都需要回头再找另一条路。 Haskell的最大乐趣之一就是人们关心法律 [1]。您会发现在Hackage上很少有定义违法实例的库。并且有充分的理由:例如,Data.Set
期望Ord
定义总订单并与Eq
兼容。具有违法的Ord
的类型会使Set
的类型完全荒谬而无用。但是我自信地将各种东西放到Set
上,而不必担心,因为在Haskell文化中,合法性非常普遍。
但是,如果您不认为...好。很抱歉传道。
[1]我很尴尬地发现预期的法律不是documented with the Ord
class。无论如何,我对这些法律的常规理解如下:
等效符号:
x < y = compare x y == LT
x > y = compare x y == GT
x == y = compare x y == EQ
x <= y = x < y || x == y
x >= y = x > y || x == y
==
是等价关系:
x == x
If x == y then y == x
If x == y and y == z then x == z
==
的可扩展性 (这似乎比其他的宽松一些;例如,sort
的文档以{{1 }}可能不是扩展性的):
==
(对于从给定抽象外部可定义的f 边界; IOW相同值的不同表示形式是 只要用户无法区分它们就可以。)
if x == y, then f x == f y
的总计:
<
(三尖瓣切开术是从“记号的等价性”与“比较”开始)
答案 1 :(得分:1)
出于我在注释中讨论的原因,我建议您不要这样做:您的Ord
实例和Eq
实例应该一致,而您的Eq
实例应该仅等于行为相同。而是查看您的数据,其中仅包含您希望比较的信息。所以:
data Constructor = Lower | Higher deriving (Eq, Ord, Read, Show)
data Wrapper = WrappedOne Foo | WrappedTwo Bar deriving (Read, Show)
constructor :: Wrapper -> Constructor
constructor (WrappedOne _) = Lower
constructor (WrappedTwo _) = Higher
现在,您将在这里致电compare wrapperA wrapperB
,而致电compare (constructor wrapperA) (constructor wrapperB)
。
答案 2 :(得分:0)
作为一种替代方法,如果您只对构造函数级别的比较感兴趣,而无需为有序组件类型提升底层的Ord
实例,则可以使用Data.Data
泛型来按构造函数索引排序:
{-# LANGUAGE DeriveDataTypeable #-}
import Data.Data
import Data.Ord
data OrderedType = One | Two | Three deriving (Eq, Ord, Show, Data)
data UnorderedType = Red | Blue | Green deriving (Eq, Show, Data)
data WrapperType = WrappedOne OrderedType
| WrappedTwo UnorderedType deriving (Eq, Show, Data)
compareCon :: (Data a) => a -> a -> Ordering
compareCon = comparing (constrIndex' . toConstr)
where constrIndex' x = case constrRep x of
AlgConstr i -> i
_ -> 0
constrIndex
模块中有一个Data.Data
,但是当类型不是代数时会抛出错误,因此上面的constrIndex'
更安全。
无论如何,有了这些定义,我们得到:
> compareCon (WrappedOne One) (WrappedTwo Red)
LT
> compareCon (WrappedOne One) (WrappedOne Two)
EQ
> compareCon (WrappedTwo Blue) (WrappedTwo Green)
EQ
>
这似乎比滥用Ord
安全得多。