为什么有些Data.List.Split函数使用`Int`和其他`Integral a`?

时间:2013-08-25 09:17:22

标签: haskell

以下是来自Data.List.Split的{​​{3}}和chunksOf的类型签名:

chunksOf :: Int -> [e] -> [[e]]
splitPlaces :: Integral a => [a] -> [e] -> [[e]]

为什么某些功能(如chunksOf)使用Int而其他功能(如splitPlaces)使用更通用的Integral a

1 个答案:

答案 0 :(得分:6)

在这个答案中,我试着看看界面不一致的历史原因。 摘要:似乎布伦特在运行中使一些函数更通用,以帮助制定QuickCheck属性。

为什么splitPlaces是通用的?

看起来Brent概括了splitPlaces的类型,以便更容易为该函数引入QuickCheck属性。 QuickCheck属性使用newtype包装器来控制测试用例生成,并且使用Integral a约束,splitPlaces可以查看此newtype包装器以进行算术运算。另见:

但是,这是关于splitPlaces的一个属性:

prop_splitPlaces_preserve :: [NonNegative Integer] -> [Elt] -> Bool
prop_splitPlaces_preserve ps l = concat (splitPlaces ps' l) == genericTake (sum ps') l
  where ps' = map unNN ps

请注意,QuickCheck会在传递给ps之前自动生成map unNN ps转换的列表splitPlacesunNN函数删除了NonNegative包装器,因此splitPlaces不必处理NonNegative包装器本身。但是,它会收到[Integer]类型的参数而不是[Int],因此它仍然需要在数字类型中是通用的。

使用[NonNegative Integer]代替[NonNegative Int]

有什么意义?

我怀疑[Int]的属性是假的,因为计算总和时算术溢出。 QuickCheck甚至可以证明该属性是可证伪的,因为Arbitrary [NonNegative Integer]实例最终会委托给arbitrarySizedBoundedIntegral,这会产生非常大的值。

我想使用[NonNegative Integer]代替以两种方式绕过这个问题:

  1. 使用Integer时,不会发生溢出。
  2. Arbitrary Integer实例委托给arbitrarySizedIntegral,只生成小值。
  3. 所以我猜允许任意积分类型的原因是Int的QuickCheck属性失败但Integer成功。

    为什么chunksOf不是通用的?

    chunksOf的属性使用模式匹配来删除newtype包装器。另见:

    以下是chunksOf

    的其中一个属性
    prop_chunksOf_all_n :: Positive Int -> NonEmptyList Elt -> Bool
    prop_chunksOf_all_n (Positive n) (NonEmpty l) = all ((==n) . length) (init $ chunksOf n l)
    

    请注意,此属性与QuickCheck自动生成的参数匹配,并在没有newtype包装的情况下将它们传递给chunksOf。对于测试chunksOf所需的参数,这很容易做到,因为这些数字不是嵌套在其他类型中的。与上面的prop_splitPlaces_preserve比较,将[NonNegative Integer]转换为[Integer][Int]需要比模式匹配更复杂的内容。

    Arbitrary IntArbitrary Integer之间的区别在这里无关紧要,因为该属性不涉及任何可以触发算术溢出的操作。