我已经创建了一个库,可以创建"列出像"序列,其中实现了许多Prelude样式函数。我想为此编写一些测试用例,以确保我的库产生正确的输出,我认为最简单的方法是编写一些函数,将结果转换为列表并将它们与Prelude结果进行比较。让我们说我们得到了这个:
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
不输出列表,而是输出自己的数据类型。
以上工作正常,但请注意我是如何重复自己的#34; (TM)。我必须确保左侧和右侧相同,否则我的测试用例是错误的。
有没有一种很好的方式来编写这样的测试用例,所以我不必重复自己?
答案 0 :(得分:1)
P.take
和ML.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
这个解决方案有点笨拙,但仍然有效。请随时改进。