什么决定了Haskell中数据构造函数的顺序?

时间:2018-11-06 19:03:34

标签: haskell

当我在Haskell上运行此代码时:

[2] <= [1,5]

我弄错了。

但是,当我运行此命令时:

data T = A | B | C T T deriving (Eq,Ord)
A <= B && B <= C A A

我得到了。

这是为什么?出于相同的原因[2] <[1,5]也是,B <= C A A不应该为假吗?

2 个答案:

答案 0 :(得分:7)

Haskell report指定了自动派生实例的外观:

  

10.1 EqOrd的派生实例

     

由的派生实例自动引入的类方法   EqOrd(==)(/=)compare(<)(<=)(>),   (>=)maxmin后七个运算符的定义如下   以字典方式比较他们的论点   给定构造函数集,数据类型中包含较早的构造函数   声明的数量要小于后来的声明

     

例如,对于Bool数据类型,我们有(True > False) == True

     

派生的比较始终从左向后遍历构造函数   对。这些示例说明了此属性:

  (1,undefined) == (2,undefined) =>    False
  (undefined,1) == (undefined,2) =>    _|_
     

EqOrd的所有派生操作都严格   论点。例如,False <= _|_ is _|_即使False是   Bool类型的第一个构造函数。

因此,Haskell将在此处实现一个Ord函数,其中无论参数如何,具有数据构造函数A的对象将小于具有数据构造函数B的对象和一个对象使用数据构造函数B的对象小于使用数据构造函数C的对象。这是因为在定义A之前先定义B

仅在数据构造函数相同的情况下,才按字典顺序对参数进行比较:因此首先比较两个对象的第一个参数,如果不相等,则是比较的结果(如果它们相等),然后我们比较第二个参数,依此类推。

因此对于您的T类型,这些实现为:

instance Ord T where
    (<=) A A = True
    (<=) A B = True
    (<=) A (C _ _) = True
    (<=) B C = True
    (<=) B (C _ _) = True
    (<=) (C xa ya) (C xb yb) = xa <= xb || (xa == xb && ya <= yb)
    (<=) _ _ = False

如果列表定义为:

data [] a = [] | (:) a ([] a)

然后,该定义也遵循列表的顺序,因为空列表小于非空列表,并且如果我们比较两个非空列表,我们首先比较第一个元素( “ cons”数据构造函数),然后在头部相等的情况下,比较尾巴(“ cons”构造函数的第二个参数)。因此,自动推导应为:

import Data.Monoid((<>))

instance Ord ([] a) where
    compare [] [] = EQ
    compare [] (_:_) = LT
    compare (_:_) [] = GT
    compare (ha:ta) (hb:tb) = compare ha hb <> compare ta tb

此处(<>)的{​​{1}}运算符采用左元素,如果它不等于Ordering,则采用右元素。

答案 1 :(得分:5)

如果一个自动生成的Ord实例的构造函数在构造函数列表中的另一个值之前,则它将比另一个值小。如果构造函数相同,则将按字典顺序成对比较元素。如果我们假设Ord位于[]之前,则列表的:实例的行为相同。

[2] <= [1,5]为假的原因是[2](又名2 : [])和[1,5](又名1:5:[])都使用相同的构造函数,因此将比较21,并且2大于1

此原因根本不适用于B <= C A ABC A A不使用相同的构造函数,因此B获胜是因为它在构造函数列表中的C之前。