首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >定义枚举子类型不是序数的Ordinal类型

定义枚举子类型不是序数的Ordinal类型
EN

Stack Overflow用户
提问于 2018-07-27 07:40:34
回答 3查看 116关注 0票数 2

我遇到了定义序数类型的问题,这些类型的值可能是序数,也可能不是序数。

基本上我有两种类型,一种是OrderedType,一种是UnorderedType

代码语言:javascript
复制
data OrderedType = One | Two | Three deriving (Eq, Ord, Show)
data UnorderedType = Red | Blue | Green deriving (Eq, Show)

我有一个类型,它的值构造函数将其中之一作为参数:

代码语言:javascript
复制
data WrapperType = WrappedOne OrderedType
                   | WrappedTwo UnorderedType deriving (Eq, Ord, Show)

基本上,我希望能够做的是在不为WrappedOneWrappedTwo实现compare函数的情况下对WrapperType进行排序。

当我尝试编译上面的代码时,我得到了以下错误:

代码语言:javascript
复制
• 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的所有值。

简而言之,我想要做的是:

代码语言:javascript
复制
WrappedOne _ < WrappedTwo _ -- True

但不需要为每种类型编写Ord实例。

我该怎么做呢?

EN

回答 3

Stack Overflow用户

发布于 2018-07-27 17:36:39

不是100%清楚你想要什么;我猜你希望所有用WrappedTwo构造的值都被认为是相等的?

代码语言:javascript
复制
newtype ForgetOrder a = ForgetOrder a

instance Eq (ForgetOrder a) where
    _ == _ = True

instance Ord (ForgetOrder a) where
    compare _ _ = EQ

然后您可以将您的类型定义为:

代码语言:javascript
复制
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。无论如何,我对这些定律的传统理解如下:

符号的

  1. Equivalence:

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.)

  • Totality of <**:** ,就允许相同值的不同表示。

如果x <= y和y <= z,则x <= z

(三分法来自于“符号的等价性”和“比较”)

票数 3
EN

Stack Overflow用户

发布于 2018-07-27 08:05:03

出于我在评论中讨论的原因,我建议您不要这么做:您的Ord实例和Eq实例应该一致,并且您的Eq实例应该只将行为相同的东西等同起来。取而代之的是,对你的数据有一个视图,其中只有你想要比较的信息。所以:

代码语言:javascript
复制
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)

票数 1
EN

Stack Overflow用户

发布于 2018-07-27 21:27:47

作为另一种方法,如果您只对构造函数级别的比较感兴趣,并且不需要提升有序组件类型的底层Ord实例,则可以使用Data.Data泛型按构造函数索引排序:

代码语言:javascript
复制
{-# 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

Data.Data模块中有一个constrIndex,但是当类型不是代数类型时,它会抛出一个错误,所以上面的constrIndex'更安全。

无论如何,通过这些定义,我们得到:

代码语言:javascript
复制
> compareCon (WrappedOne One) (WrappedTwo Red)
LT
> compareCon (WrappedOne One) (WrappedOne Two)
EQ
> compareCon (WrappedTwo Blue) (WrappedTwo Green)
EQ
> 

这似乎比滥用Ord安全得多。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51548887

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档