为什么Haskell给出“模糊类型变量”错误?

时间:2012-12-15 01:04:05

标签: list function haskell recursion quickcheck

过去的纸质问题问我;定义函数p :: [a] - > [a]交换列表中的每两项。您的函数应该将第一个与第二个项目交换,第三个项目 与第四,等等。通过列表理解来定义另一个通过递归。

这就是我想出来的:

import Test.QuickCheck
p :: [a] -> [a]
p [] = []
p xs = concat [ [y,x] | ((x,y),i) <- zip (zip xs (tail xs)) [1..], odd i]
q :: [a] -> [a]
q [] = []
q (x:y:zs) | even (length zs) = [y,x] ++ q zs
           | otherwise = error "The list provided is not of even length"
prop_2 xs = even (length xs) ==> p xs == q xs
check2 = quickCheck prop_2

这些功能运行正常,但我想检查两者是否相同,所以我把quickCheck放在下面;但出于某种原因,这给了我一个错误 “使用prop_2”产生的模糊类型变量[a0]

我只是不明白这里有什么不对,我看起来对我很明智...... 什么是Haskell抱怨?

1 个答案:

答案 0 :(得分:7)

让我们首先评论check2并向GHCi询问prop_2的类型:

*Main> :t prop_2
prop_2 :: Eq a => [a] -> Property

好的,所以你编写prop_2的方式,它适用于相等类中任何元素类型的列表。

现在,您希望将prop_2传递给quickCheck函数。我们来看看quickCheck下一个的类型:

*Main> :t quickCheck
quickCheck :: Testable prop => prop -> IO ()

这个功能实际上有一个非常普遍的类型。它适用于Testable类中的任何内容。那么这个Testable类如何工作呢?这里有几个基本实例,例如:

instance Testable Bool
instance Testable Property  -- this is a simplification, but it's more or less true

这些实例在QuickCheck库中定义,并告诉您可以快速检查 常数布尔值以及属性类型的元素。

现在,测试不依赖于任何输入的属性并不是特别有趣。有趣的例子就是这个:

instance (Arbitrary a, Show a, Testable prop) => Testable (a -> prop)

这说明如果您知道如何生成特定类型的随机值(Arbitrary a)以及如何显示该类型的值(Show a),那么您还可以测试来自的函数键入a到已测试类型prop

为什么呢?因为这就是QuickCheck的运作方式。在这种情况下,QuickCheck将咨询Arbitrary实例,以便提出类型为a的随机测试用例,将函数应用于每个实例,并检查结果是否为正。如果任何测试失败,它将打印一条消息,通知您测试失败,并打印测试用例(这就是为什么还有Show要求。)

现在,在我们的情况下,这意味着我们应该能够快速检查prop_2:它是一个导致Property的函数。重要的是函数参数([a]类型只要Eq a成立)就是类Arbitrary的成员和类Show的成员。

这里我们得出错误的来源。现有信息不足以得出这一结论。正如我在开头所说,prop_2适用于允许相等的任何元素类型的列表。没有内置规则表明所有这些类型都在ArbitraryShow中。但即使有,QuickCheck应该生成什么样的列表?它应该生成布尔列表,单位类型列表,函数列表,字符列表,整数列表吗?如此多的选项以及元素类型的选择可能会影响您是否发现了错误。 (考虑到GHC会仅使用一个元素选择单位类型()。然后,您的属性将保留任何两个函数pq,保留输入列表的长度,无论是否他们有你想要的交换财产。)

这就是为什么你需要向GHC提供额外的类型信息,以便它可以解析用于列表的元素类型。这样做很简单。您可以注释prop_2本身:

prop_2 :: [Integer] -> Property

或者,如果您不希望这样(因为您可能希望在不重新实现prop_2的情况下在不同类型的列表上运行测试),您可以在调用quickCheck时添加类型注释:< / p>

check2 = quickCheck (prop_2 :: [Integer] -> Property)

现在代码编译,我们可以运行check2

Main*> check2
+++ OK, passed 100 tests.