2013-08-25 39 views
5

下面是chunksOfsplitPlaces類型簽名從Data.List.Split爲什麼一些Data.List.Split函數使用`Int`和其他`Integral a`?

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

爲什麼有些函數(如chunksOf)使用Int而其他(如splitPlaces)使用更通用的Integral a

+0

好問題!從界面的角度來看,我沒有看到任何特別的好理由 - 可能只是稍微簡單的實現。可以使用標準化。哪一個更好取決於前景,分歧會比比皆是。 – luqui

回答

6

在這個答案中,我試圖看看不一致接口的歷史原因。 摘要:似乎布倫特使一些功能更加通用,以幫助制定QuickCheck屬性。

爲什麼splitPlaces是通用的?

它看起來好像Brent將splitPlaces的類型概括爲便於爲該函數引入QuickCheck屬性。 QuickCheck屬性使用newtype包裝來控制測試用例的生成,並且通過Integral a約束,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其通過map unNN ps被傳遞到splitPlaces之前轉換。 unNN函數刪除了NonNegative包裝,因此splitPlaces不必處理NonNegative包裝本身。但是,它收到類型[Integer]而不是[Int]的參數,所以它仍然需要在數字類型中是通用的。

使用[NonNegative Integer]而不是[NonNegative Int]有什麼意義?

我懷疑[Int]的屬性是錯誤的,因爲計算總和時的算術溢出。該屬性甚至可以通過QuickCheck進行驗證,因爲Arbitrary [NonNegative Integer]實例最終將委託給arbitrarySizedBoundedIntegral,這可能會生成非常大的值。

我猜,使用[NonNegative Integer],而不是通過兩種方式規避這個問題:

  1. 隨着Integer,不會發生溢出。
  2. Arbitrary Integer實例代表arbitrarySizedIntegral只生成小值。

所以我想,允許任意積分類型的原因是QuickCheck屬性將失敗的Int但成功爲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) 

注意,這家酒店就由快速檢查自動生成的參數匹配,並將它們傳遞到chunksOf沒有NEWTYPE包裝。對於測試chunksOf所需的參數,這很容易實現,因爲這些數字不是嵌套在其他類型中。與上面的prop_splitPlaces_preserve相比,將[NonNegative Integer]轉換爲[Integer][Int]需要比模式匹配更復雜的事情。

Arbitrary IntArbitrary Integer之間的差異在這裏並不重要,因爲該屬性不涉及任何可能觸發算術溢出的操作。

相關問題