【问题标题】:Why is Haskell giving "ambiguous type variable" error?为什么 Haskell 给出“模糊类型变量”错误?
【发布时间】:2012-12-03 00:35:37
【问题描述】:

一个过去的论文问题问我;定义一个函数 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 到底在抱怨什么?

【问题讨论】:

  • 想象你是 GHC。您为 QuickCheck 测试生成什么类型​​的列表?

标签: list function haskell recursion quickcheck


【解决方案1】:

我们先把check2注释掉,然后问GHCiprop_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),那么您还可以测试来自该类型的函数 @987654335 @ 到一个已经可以测试的类型prop

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

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

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

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

prop_2 :: [Integer] -> Property

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

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

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

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

【讨论】:

  • 您好,非常感谢您的回答!但我还是不明白(对不起,我是一个初学者,刚开始用 uni 编程,现在让我很生气......),Haskell 不知道“prop_2”是什么我必须告诉它吗? >prop_2 xs = even (length xs) ==> p xs == q xs 将 [a] 转换为 Int 然后给出 Bool,所以 >p :: [a] -> Int -> Bool 但我'不知道在此之后要为 "==> p xs == q xs" 说明什么 ...
  • @Brad 我已经大大扩展了答案。我希望这会有所帮助。
猜你喜欢
  • 1970-01-01
  • 2013-12-13
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 2019-04-22
  • 1970-01-01
  • 2021-11-18
  • 1970-01-01
相关资源
最近更新 更多