【问题标题】:Test.QuickCheck.Monadic: why is assert applied to Bool, not Testable a => aTest.QuickCheck.Monadic:为什么断言应用于 Bool,而不是 Testable a => a
【发布时间】:2015-07-29 13:29:07
【问题描述】:

Testing Monadic Code with QuickCheck (Claessen, Hughes 2002) 中,assert 的类型为:

assert :: (Monad m, Testable a) => a -> PropertyM m ()

但是,在Test.QuickCheck.Monadic 中,它的类型为:

assert :: (Monad m) => Bool -> PropertyM m ()

为什么assert在库中有后一种类型?

【问题讨论】:

  • 也许源代码中的 cmets 会有所帮助:(link)。有趣的是assert 的评论有论文的签名,stop 的签名也基本相同。

标签: haskell quickcheck


【解决方案1】:

我认为这是由于技术限制,因为目前要使用Test.QuickCheck 库评估Testable,您需要使用quickCheck* 函数之一,它非常以IO 为中心。发生这种情况是因为 QuickCheck 通过随机生成可能的输入(默认为 100)来测试 Testable 属性,试图找到证明属性为假的 counterexample。如果未找到此类输入,则假定该属性为真(尽管这不一定是事实;可能存在未测试的反例)。为了能够在 Haskell 中生成随机输入,我们坚持使用 IO monad。

请注意,尽管assert 是用这种通用方式定义的,但它在整篇论文中仅与Bool 一起使用。因此库作者(与论文相同)宁愿牺牲通用的Testable 参数来换取简单的Bool,此时不强制任何monad。

而且我们可以看到他们甚至在source code中写了原始签名作为评论:

-- assert :: Testable prop => prop -> PropertyM m ()

还要注意,尽管stop 函数具有相似的签名:

stop :: (Testable prop, Monad m) => prop -> PropertyM m a

与本文中的assert 函数相同,因为前者将在两种情况下停止计算,条件是TrueFalse。另一方面,assert 只会在条件为False 时停止计算:

⟦ 断言 True ≫ p ⟧ = ⟦ p ⟧

⟦ 断言 False ≫ p ⟧ = { 返回 False }

我们可以很容易地从论文中编写IO 版本的assert 函数:

import Control.Monad
import Control.Monad.Trans
import Test.QuickCheck
import Test.QuickCheck.Monadic
import Test.QuickCheck.Property
import Test.QuickCheck.Test

assertIO :: Testable prop => prop -> PropertyM IO ()
assertIO p = do r <- liftIO $ quickCheckWithResult stdArgs{chatty = False} p
                unless (isSuccess r) $ fail "Assertion failed"

现在我们可以做一个测试,看看assertIOstop之间的区别:

prop_assert :: Property
prop_assert = monadicIO $ do assertIO succeeded
                             assertIO failed

prop_stop :: Property
prop_stop = monadicIO $ do stop succeeded
                           stop failed

main :: IO ()
main = do putStrLn "prop_assert:"
          quickCheck prop_assert
          putStrLn "prop_stop:"
          quickCheck prop_stop

succeededfailed 可以分别替换为 TrueFalse。这只是为了表明现在我们不仅限于Bool,而是可以使用任何Testable

输出是:

prop_assert:
*** 失败的!断言失败(经过 1 次测试):
道具停止:
+++ 好的,通过了 100 次测试。

正如我们所见,尽管第一个 assertIO 成功,prop_assert 由于第二个 assertIO 而失败。另一方面,prop_stop 通过了测试,因为第一个stop 成功并且计算已经停止,而不是测试第二个stop

【讨论】:

  • 但是quickCheck* 函数“非常以 IO 为中心”的原因是什么,如果它导致放弃论文提出的库中的一些通用性。
  • @frasertweedale 问题在于 QuickCheck 通过随机生成可能的输入(默认为 100)来测试 Testable 属性,试图找到证明属性为假的 counterexample。如果未找到此类输入,则假定该属性为真(尽管这不一定是事实;可能存在未测试的反例)。为了能够在 Haskell 中生成随机输入,我们坚持使用 IO monad。另请注意,即使assert 以这种通用方式定义,它在所有论文中也仅与Bool 一起使用。
  • 好的,开始变得更清楚了..上述几点最好包含在您的答案中。
猜你喜欢
  • 2016-08-07
  • 1970-01-01
  • 1970-01-01
  • 2014-03-29
  • 2019-08-13
  • 1970-01-01
  • 2011-09-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多