以下是来自Data.List.Split
的{{3}}和chunksOf
的类型签名:
chunksOf :: Int -> [e] -> [[e]]
splitPlaces :: Integral a => [a] -> [e] -> [[e]]
为什么某些功能(如chunksOf
)使用Int
而其他功能(如splitPlaces
)使用更通用的Integral a
?
答案 0 :(得分:6)
在这个答案中,我试着看看界面不一致的历史原因。 摘要:似乎布伦特在运行中使一些函数更通用,以帮助制定QuickCheck属性。
splitPlaces
是通用的?看起来Brent概括了splitPlaces
的类型,以便更容易为该函数引入QuickCheck属性。 QuickCheck属性使用newtype包装器来控制测试用例生成,并且使用Integral a
约束,splitPlaces
可以查看此newtype包装器以进行算术运算。另见:
The patch
它概括了splitPlaces
的类型并引入了QuickCheck属性。
The QuickCheck documentation关于这些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
转换的列表splitPlaces
。 unNN
函数删除了NonNegative
包装器,因此splitPlaces
不必处理NonNegative
包装器本身。但是,它会收到[Integer]
类型的参数而不是[Int]
,因此它仍然需要在数字类型中是通用的。
[NonNegative Integer]
代替[NonNegative Int]
?我怀疑[Int]
的属性是假的,因为计算总和时算术溢出。 QuickCheck甚至可以证明该属性是可证伪的,因为Arbitrary [NonNegative Integer]
实例最终会委托给arbitrarySizedBoundedIntegral
,这会产生非常大的值。
我想使用[NonNegative Integer]
代替以两种方式绕过这个问题:
Integer
时,不会发生溢出。Arbitrary Integer
实例委托给arbitrarySizedIntegral
,只生成小值。所以我猜允许任意积分类型的原因是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 Int
和Arbitrary Integer
之间的区别在这里无关紧要,因为该属性不涉及任何可以触发算术溢出的操作。