Haskell 为什么一些Data.List.Split函数使用'Int'而其他函数使用'Integral a'?
以下是来自Haskell 为什么一些Data.List.Split函数使用'Int'而其他函数使用'Integral a'?,haskell,Haskell,以下是来自Data.List.Split的类型签名: chunksOf :: Int -> [e] -> [[e]] splitPlaces :: Integral a => [a] -> [e] -> [[e]] 为什么有些函数(如chunksOf)使用Int,而另一些函数(如splitPlaces)使用更通用的Integral a?在这个答案中,我试图找出接口不一致的历史原因。概要:Brent似乎在运行中使一些函数更通用,以帮助制定QuickCheck属性 为
Data.List.Split的类型签名:
chunksOf :: Int -> [e] -> [[e]]
splitPlaces :: Integral a => [a] -> [e] -> [[e]]
为什么有些函数(如chunksOf
)使用Int
,而另一些函数(如splitPlaces
)使用更通用的Integral a
?在这个答案中,我试图找出接口不一致的历史原因。概要:Brent似乎在运行中使一些函数更通用,以帮助制定QuickCheck属性
为什么splitPlaces
是通用的?
Brent似乎推广了splitPlaces
的类型,以便更容易为该函数引入QuickCheck属性。QuickCheck属性使用新类型包装器来控制测试用例的生成,并且使用Integral a
约束,splitPlaces
可以通过这个新类型包装器查看算术运算。另见:
-
这概括了
splitPlaces
的类型,并引入了快速检查属性
- 关于这些新型包装
但是,以下是关于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
请注意,快速检查会自动生成列表ps
,该列表在传递到splitPlaces
之前由map unNN ps
转换。unNN
函数删除非负
包装,因此splitPlaces
不必处理非负
包装本身。但是,它接收的参数类型是[Integer]
,而不是[Int]
,因此它在数值类型中仍然需要是泛型的
用[非负整数]
代替[非负整数]
有什么意义?
我怀疑[Int]
的属性为false,因为计算和时出现算术溢出。该属性甚至可能被QuickCheck伪造,因为任意[NonNegative Integer]
实例最终将委托给该实例,该实例可以生成非常大的值
我想使用[非负整数]
可以从两个方面解决这个问题:
使用整数
,不会发生溢出
任意整数
实例委托给只生成小值的实例
所以我猜允许任意整数类型的原因是,QuickCheck属性对于Int
会失败,但是对于Integer
会成功
为什么chunksOf
不是通用的?
chunksOf
的属性使用模式匹配删除新类型包装。另见:
- 这将为
splitEvery
引入属性
- 将
splitEvery
重命名为chunksOf
以下是关于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自动生成的参数匹配,并将其传递给chunksOf
,而不使用newtype包装器。对于测试chunksOf
所需的参数,这很容易做到,因为这些数字没有嵌套在其他类型中。与上面的prop\u splitPlaces\u preserve
相比,将[非负整数]
转换为[整数]
或[Int]
需要比模式匹配更复杂的东西
arbitral Int
和arbitral Integer
之间的差异在这里并不重要,因为该属性不涉及任何可能触发算术溢出的操作。问得好!从接口的角度来看,我看不出有什么特别好的理由——也许只是实现起来稍微简单一点。可以使用标准化。两者中哪一个更好取决于前景,分歧将比比皆是。