我遇到了定义序数类型的问题,这些类型的值可能是序数,也可能不是序数。
基本上我有两种类型,一种是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)基本上,我希望能够做的是在不为WrappedOne和WrappedTwo实现compare函数的情况下对WrapperType进行排序。
当我尝试编译上面的代码时,我得到了以下错误:
• 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)这是有道理的,因为WrappedType的股票派生Ord实例将尝试比较WrappedTwo的所有值。
简而言之,我想要做的是:
WrappedOne _ < WrappedTwo _ -- True但不需要为每种类型编写Ord实例。
我该怎么做呢?
发布于 2018-07-27 17:36:39
不是100%清楚你想要什么;我猜你希望所有用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)是的,包装和解包newtype确实有点麻烦,而且编写模式同义词来避免它也很麻烦。这就是生活。
然而,我担心你也想要WrappedTwo Red /= WrappedTwo Green。在这一点上,我将不得不与瓦格纳先生一起追赶潮流,并说,无论你正在穿越的思想道路需要这一点,回头找另一条路。Haskell最大的乐趣之一是人们关心法律1.你会发现Hackage上很少有定义违法实例的库。这是有充分理由的:例如,Data.Set希望Ord定义一个总顺序,并与Eq兼容。带有违法Ord的类型会使该类型的Set变得完全无意义和损坏。但我自信地把所有的东西都放在Set里,而不用担心这个世界,因为在哈斯克尔文化中,法律是如此无处不在。
但如果你不是这么想的话。好的。很抱歉我这么说教。
1我很尴尬地发现,预期的定律并不是documented with the Ord class。无论如何,我对这些定律的传统理解如下:
符号的
Xy=比较x y == GT x == y=比较x y == EQ x <= y=x= y=x|y || x == 是一个等价关系:
X == x If x == y then y == x If x == y和y == z then x == (这个看起来比其他的要宽松一些;例如,的文档预先假设==不是可扩展的):
如果x == y,则f x == f y
(对于f,可以在给定的抽象边界之外定义;只要用户不能告诉他们apart.)
<**:** ,就允许相同值的不同表示。如果x <= y和y <= z,则x <= z
(三分法来自于“符号的等价性”和“比较”)
发布于 2018-07-27 08:05:03
出于我在评论中讨论的原因,我建议您不要这么做:您的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)。
发布于 2018-07-27 21:27:47
作为另一种方法,如果您只对构造函数级别的比较感兴趣,并且不需要提升有序组件类型的底层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
_ -> 0Data.Data模块中有一个constrIndex,但是当类型不是代数类型时,它会抛出一个错误,所以上面的constrIndex'更安全。
无论如何,通过这些定义,我们得到:
> compareCon (WrappedOne One) (WrappedTwo Red)
LT
> compareCon (WrappedOne One) (WrappedOne Two)
EQ
> compareCon (WrappedTwo Blue) (WrappedTwo Green)
EQ
> 这似乎比滥用Ord安全得多。
https://stackoverflow.com/questions/51548887
复制相似问题