使用newtype包装创建Ord实例

时间:2016-08-01 01:21:13

标签: haskell typeclass newtype

给定数据类型,例如

data Foo = Bar | Baz | Qux

我希望这种类型有多种不同的排序,以下是实现这一目标的最常见/标准方式吗?

newtype FooPriorityA = FooPriorityA { unFooPriorityA :: Foo }

instance Ord FooPriorityA where
    compare (FooPriorityA x) (FooPriorityA y) = f x `compare` f y
        where f :: Foo -> Int
              f Baz = 1
              f Bar = 2
              f Qux = 3

newtype FooPriorityB = FooPriorityB ... and so on

委托Int的Ord实例那样疯狂吗?与写compare的n ^ 2比较相比,感觉更安全,工作量更少。

我可能忽略了任何明显的缺陷" hack"?它甚至是一个" hack"?

3 个答案:

答案 0 :(得分:7)

这绝对合理。事实上,您可以使用the comparing function from Data.Ord使其更加直接:

instance Ord FooPriorityA where
  compare (FooPriorityA x) (FooPriorityA y) = comparing f x y
    where f :: Foo -> Int
          f Baz = 1
          f Bar = 2
          f Qux = 3

虽然取决于您的偏好,compare的版本可能更符合您的喜好。

答案 1 :(得分:5)

只要您指定案例,您只需要O( n )(2 n - 1,确切地说)定义来指定总订单增加优先顺序:

instance Ord FooPriorityA where
    compare (FooPriorityA x) (FooPriorityA y) | x == y = EQ   -- Use the Eq instance
                                              -- Bar is the smallest
                                              | x == Bar = LT
                                              | y == Bar = GT
                                              -- Baz is the next smallest
                                              | x == Baz = LT
                                              | y == Baz = GT
                                              -- Proceed in the desired order.
                                              -- You don't need to say anything
                                              -- explicit about the largest value

第一种情况(x == y)涵盖O(n)种可能性。下面的每个案例可能看起来都不完整,但它带有隐含的信息,即每个前面的案例都是错误的。例如,x == Bar = LT并不意味着{em> x == Bar评估为LT的每个案例;已处理x == Bar && y == Bar的情况,因此第二种情况实际上是隐式x == Bar && y /= Bar

同样,案例4(x == Baz)假设y /= Baz(由案例1未能匹配)和y /= Bar(假设案例3未能匹配) )。因此,y的任何剩余可能值实际上都大于Baz

你去的列表越往下,剩下的未处理案例就越少。到最后,你不需要对最大的项目做任何明确的说明;关于它与其他 n 的比较的所有信息 - 前面的案例已经捕获了1个项目。

另外,请记住,f的定义是fromEnum类实例的最小实现的一半(Enum),然后您可以使用它来定义你的Ord个实例。

instance Enum FooPriorityA where
     fromEnum Bar = 1
     fromEnum Baz = 2
     fromEnum Qux = 3
     toEnum 1 = Bar
     toEnum 2 = Baz
     toEnum 3 = Qux

instance Ord FooPriorityA where
    compare x y = compare (fromEnum x) (fromEnum y)
    -- compare = compare `on` Data.Function.fromEnum

答案 2 :(得分:0)

如何使函数根据表示所需顺序的列表生成从FooInt的映射:

import Data.List

data Foo = Bar | Baz | Qux deriving Eq

newtype FooPriorityA = FooPriorityA { unFooPriorityA :: Foo } deriving Eq

fooOrdering :: [Foo] -> Foo -> Foo -> Ordering
fooOrdering orderedList a b = f a `compare` f b
    where f x = elemIndex x orderedList

instance Ord FooPriorityA where
    compare (FooPriorityA x) (FooPriorityA y) = fooOrdering [Baz, Bar, Qux] x y

您甚至可以添加一个断言,以确保所有元素在列表中只出现一次。