2017-02-20 38 views
2

我製作了一個創建「list like」序列的庫,其中實現了許多Prelude樣式函數。我想爲此編寫一些測試用例,以確保我的庫生成正確的輸出,並且我認爲最簡單的方法是編寫一些函數,將結果轉換爲列表並將它們與Prelude結果進行比較。比方說,我們已經有了這樣的:在Haskell中爲不同類型編寫測試用例

import qualified MyLibrary as ML 
import qualified Prelude as P 

例如我可能要在以下測試用例:

P.take 5 (P.enumFrom 1) == toList (ML.take 5 (ML.enumFrom 1)) 

注意ML.enumFrom不輸出列表,它輸出它自己的數據類型。

上述工作正常,但請注意我是如何「重複自己」(商標)。我必須確保左側和右側是相同的,否則我的測試用例是錯誤的。

有沒有一種很好的方式來編寫像這樣的測試用例,所以我不必重複自己?

+1

當你的測試被定義​​爲「重複自己」以便比較兩種機制時,我不認爲你真的「重複自己」。 –

+0

理想情況下,所有的測試都需要一個參數來決定使用哪個函數,這樣我就可以擁有一個'doTest'函數,就像'doTest test = test True == toList(test False)'或類似的東西,但我沒有' t找到了一種方式來做到這一點,而不會讓類型分析者恨我。 – Clinton

+0

@Clinton:我認爲如果沒有實例化一個普通的'class'或可能使用模板haskell(我沒有經驗),你無法實現 – jakubdaniel

回答

1

第一個問題,P.takeML.take等,只是看起來相似 - 事實上它們是完全不相關的函數,編譯器不知道它們的共同行爲。因此,作爲@ jd823592提出的,我們需要把它們組一個類型類(我用一個簡單的newtype包裝,因此例子是編譯):

import Prelude hiding (take, enumFrom) 
import qualified Prelude as P (take, enumFrom) 

newtype MySeq a = MySeq [a] 

class Sequence s where 
    take :: Int -> s a -> s a 
    enumFrom :: Enum a => a -> s a 
    toList :: s a -> [a] 

instance Sequence MySeq where 
    take n (MySeq xs) = MySeq (P.take n xs) 
    enumFrom n = MySeq (P.enumFrom n) 
    toList (MySeq xs) = xs 

instance Sequence [] where 
    take = P.take 
    enumFrom = P.enumFrom 
    toList = id 

然後,我們將嘗試定義使用現在已經統一了一些測試函數來自類定義。他們可能會生成任何類型的Sequence,然後我們會強制它們生成明確的類型。

test1 = doTest (take 5 $ enumFrom 1) -- the part in brackets is polymorphic 

doTest :: (Eq a, Sequence s) => s a -> Bool 
doTest test = ??? 

現在第二個問題是我們通過一個多態函數作爲參數,然後需要用不同類型的參數(在這種情況下[a]MySeq a),以實例化。在標準的Haskell 2010是不可能的,但我們可以利用這個Rank2 (or RankN) extension

{-# LANGUAGE Rank2Types #-} 

<...> 

doTest :: forall a . Eq a => (forall s . Sequence s => s a) -> Bool 
doTest test = (test `asTypeOf` dummy1) == toList (test `asTypeOf` dummy2) where 
    dummy1 :: Eq a => [a] 
    dummy1 = undefined 
    dummy2 :: Eq a => MySeq a 
    dummy2 = undefined 

該解決方案是一個有點笨拙,但仍然有效。請隨時改善。

相關問題