2017-02-20 47 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 

该解决方案是一个有点笨拙,但仍然有效。请随时改善。

相关问题