【问题标题】:Haskell: Understanding QuickCheck with higher order functionHaskell:用高阶函数理解 QuickCheck
【发布时间】:2019-11-10 07:39:15
【问题描述】:

我有函数foo:

foo :: [a] -> (a -> b) -> [b]
foo [] f = []
foo (x:xs) f = foo xs f

以及它必须满足的以下两个属性:

prop_1 :: [Int] -> Bool
prop_1 xs = foo xs id == xs 

prop_2 :: [Int] -> (Int -> Int) -> (Int -> Int) -> Bool
prop_2 xs f g = foo (foo xs f) g == foo xs (g . f)

当我尝试使用 quickCheck 测试该功能时,我收到以下错误:

 Ambiguous type variable 't0' arising from a use of '=='
      prevents the constraint '(Eq t0)' from being solved.
      Probable fix: use a type annotation to specify what 't0' should be.
      These potential instances exist:
        instance (Eq a, Eq b) => Eq (Either a b)
          -- Defined in 'Data.Either'
        instance Eq GeneralCategory -- Defined in 'GHC.Unicode'
        instance Eq Ordering -- Defined in 'ghc-prim-0.5.0.0:GHC.Classes'
        ...plus 24 others
        ...plus 107 instances involving out-of-scope types
        (use -fprint-potential-instances to see them all)
      In the expression: foo (foo xs f) g == foo xs (g . f)
      In an equation for 'prop_2':
          prop_2 xs f g = foo (foo xs f) g == foo xs (g . f)
Failed, modules loaded: none.

我不确定为什么会收到此错误以及如何解决它。任何见解都值得赞赏。

【问题讨论】:

  • 您是否在 shell 中编写了prop_2?你有没有用:{和:}把签名和函数写在同一个“块”里?
  • 不,我在一个带有 prop_1 和 foo 的文件中有 prop_2。
  • 看起来 Haskell 似乎无法理解 foo xs (g . f) 的类型,所以这通常意味着类型签名在这里没有说它是 [Int]。
  • 你能分享一个可编译的例子来重现这个问题吗?
  • 我尝试编译和运行您的属性。我无法重现您的错误消息,但我发现了另外两个问题:首先,您的函数foo 等效于const [],因此您的第一个属性将始终失败。其次,prop_2 不能被quickCheck 运行,因为没有Show (Int -> Int) 实例(quickCheck 需要这个,所以它可以打印反例)

标签: haskell functional-programming quickcheck property-based-testing


【解决方案1】:

我能够使用以下程序复制您的错误消息。注意foo 的签名被注释掉了:

import Test.QuickCheck
import Text.Show.Functions

-- foo :: [a] -> (a -> b) -> [b]
foo [] f = []
foo (x:xs) f = foo xs f

prop_1 :: [Int] -> Bool
prop_1 xs = foo xs id == xs

prop_2 :: [Int] -> (Int -> Int) -> (Int -> Int) -> Bool
prop_2 xs f g = foo (foo xs f) g == foo xs (g . f)

main = do
  quickCheck prop_1
  quickCheck prop_2

如果我把 foo 签名放回去,它的类型检查正常。测试当然会失败,因为 foo 没有按照您的意愿去做。

问题是您这里的foo 版本具有推断签名:

foo :: [a] -> b -> [c]

因此在prop_2 中,无法推断最顶层foo 调用的列表元素的类型来解析正确的(==) 操作。

如果您将foo 替换为正确的版本:

foo :: [a] -> (a -> b) -> [b]
foo [] f = []
foo (x:xs) f = f x : foo xs f

然后测试通过并且您实际上可以再次注释掉签名,因为可以推断出正确的类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-21
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 2011-12-13
    • 2012-10-15
    相关资源
    最近更新 更多