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

Bra*_*rad 3 recursion haskell function list 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
Run Code Online (Sandbox Code Playgroud)

功能正常,但我想检查两者是否相同,所以我把quickCheck放在下面; 但是由于某种原因,这给了我一个错误,说"使用prop_2引起的模糊类型变量[a0]"

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

kos*_*kus 8

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

*Main> :t prop_2
prop_2 :: Eq a => [a] -> Property
Run Code Online (Sandbox Code Playgroud)

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

现在,您想要传递prop_2给该quickCheck函数.我们来看看quickCheck下一个的类型:

*Main> :t quickCheck
quickCheck :: Testable prop => prop -> IO ()
Run Code Online (Sandbox Code Playgroud)

这个函数实际上有一个非常普遍的类型.它适用于Testable课堂上的任何事情.那这Testable节课怎么样?这里有几个基本实例,例如:

instance Testable Bool
instance Testable Property  -- this is a simplification, but it's more or less true
Run Code Online (Sandbox Code Playgroud)

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

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

instance (Arbitrary a, Show a, Testable prop) => Testable (a -> prop)
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

prop_2 :: [Integer] -> Property
Run Code Online (Sandbox Code Playgroud)

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

check2 = quickCheck (prop_2 :: [Integer] -> Property)
Run Code Online (Sandbox Code Playgroud)

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

Main*> check2
+++ OK, passed 100 tests.
Run Code Online (Sandbox Code Playgroud)