繁体   English   中英

如何编写quickCheck函数的属性?

[英]How to write quickCheck on properties of functions?

我正在尝试在Haskell Book(第15章,“Monoid,Semigroup”)中进行Monoid练习之一,但我被卡住了。 给出以下内容:

newtype Combine a b =
  Combine { unCombine :: (a -> b) }

我应该为Combine编写Monoid实例。

我写了这样的东西:

instance (Semigroup b) => Semigroup (Combine a b) where
  Combine { unCombine = f } <> Combine { unCombine = g } =
    Combine { unCombine = \x -> f x <> g x }

instance (Monoid b) => Monoid (Combine a b) where
  mempty = Combine { unCombine = \_ -> mempty }
  mappend = (<>)

但我不知道如何为实例编写quickCheck

这是我的尝试(不编译):

monoidLeftIdentity1 :: (Eq m, Monoid m) => m -> Bool
monoidLeftIdentity1 x = mappend mempty x == x

monoidRightIdentity1 :: (Eq m, Monoid m) => m -> Bool
monoidRightIdentity1 x = mappend x mempty == x


main :: IO ()
main = do 
  quickCheck (monoidLeftIdentity1 :: Combine Int (Sum Int) -> Bool)
  quickCheck (monoidRightIdentity1 :: Combine Int (Sum Int) -> Bool)

看来我必须在这种类型上实例化ArbitraryEq ,但是如何为函数编写它们呢?

有一个类似的问题 ,在那个问题中,我们被要求为Combine编写Semigroup实例。

首先是完整的代码示例:

module Main where

import Test.QuickCheck
import Data.Monoid

newtype Combine a b = Combine { unCombine :: a -> b }

instance (Semigroup b) => Semigroup (Combine a b) where
    a <> _ = a
--  (Combine f) <> (Combine g) = Combine $ \a -> (f a) <> (g a)

instance (Monoid b) => Monoid (Combine a b) where
  mempty = Combine $ \_ -> mempty

monoidLeftIdentity :: (Eq m, Monoid m) => m -> Bool
monoidLeftIdentity m = mappend mempty m == m

monoidRightIdentity :: (Eq m, Monoid m) => m -> Bool
monoidRightIdentity m = mappend m mempty == m

monoidLeftIdentityF :: (Eq b, Monoid m) => (Fun a b -> m) -> (m -> a -> b) -> a -> Fun a b -> Bool
monoidLeftIdentityF wrap eval point candidate = eval (mappend mempty m) point == eval m point 
 where m = wrap candidate

monoidRightIdentityF :: (Eq b, Monoid m) => (Fun a b -> m) -> (m -> a -> b) -> a -> Fun a b -> Bool
monoidRightIdentityF wrap eval point candidate = eval (mappend m mempty) point == eval m point 
 where m = wrap candidate

main :: IO ()
main = do
  quickCheck $ (monoidLeftIdentityF (Combine . applyFun) unCombine :: Int -> Fun Int (Sum Int) -> Bool)
  quickCheck $ (monoidRightIdentityF (Combine . applyFun) unCombine :: Int -> Fun Int (Sum Int) -> Bool)

我们在这里做什么?

首先,我们需要一种生成随机函数的方法。 也就是说,这个Fun事情是什么。 如果有一些实例可用于ab ,则有一个Fun abArbitrary实例。 但大部分时间我们都有。

可以显示Fun ab类型的值,因此Fun ab具有show实例,前提ab有一个。 我们可以用applyFun提取函数。

为了使QuickCheck能够利用这一点,我们需要提供一个Testable ,其中所有参数位置都可以随机生成和显示。

因此,我们必须根据abFun ab制定我们的属性。

为了将所有这些与Combine连接起来,我们提供了从Fun abCombine ab

现在我们遇到了另一个问题。 我们无法比较函数,因此我们无法比较Combine ab类型的值是否相等。 由于我们已经在随机生成测试用例,为什么不随机生成用于测试函数相等性的点。 平等不会是肯定的,但我们正在寻找可证实的例子! 这对我们来说已经足够了。 要做到这一点,我们提供了“应用”类型的值的函数Combine ab到类型的值a ,拿到类型的值b ,可以为希望进行相等性比较。

您可以使用Test.QuickCheck.Function生成随机函数值,因此您应该能够编写类似下面的内容来处理Arbitrary约束:

quickCheck (monoidLeftIdentity1 . Combine . apply :: Fun Int (Sum Int) -> Bool)

但是,对于Eq约束,您将无法比较函数值。 我认为仅仅检查某些输入采样的逐点相等就足够了,例如

funoidLeftIdentity1 :: (Monoid b, Eq b) => Fun a b -> a -> Bool
funoidLeftIdentity1 (Fn f) x = uncombine (Combine f <> mempty) x == uncombine mempty x

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM