如何使用QuickCheck为StateT编写测试

时间:2017-04-14 10:19:26

标签: haskell quickcheck

StateT位于Control.Monad.Trans.State.Lazy

内部的函数和m被更高的kinded使其变得难以

{-# LANGUAGE FlexibleContexts #-}
import Test.QuickCheck

newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }
instance (CoArbitrary s, Arbitrary s, Arbitrary a) => 
    Arbitrary (StateT s (Maybe) a) where -- doesn't quite work
    arbitrary = undefined

我想这样做的原因是因为我想检查使用QuickCheck,如果我编写的StateT的应用实例(用于练习)是正确的

编辑: 好的,这是我要测试的实例(假设不正确)

instance (Monad m) => Applicative (StateT s m) where
    pure x = StateT (\s -> (\a -> (a, s)) <$> pure x)
    StateT smfs <*> StateT smas = StateT $ \s -> liftA2 (\ (f, s) (a, _) -> (f a, s)) (smfs s) (smas s)

1 个答案:

答案 0 :(得分:3)

你的问题非常有趣。实际上,使用QuickCheck StateT monad变换器验证functor / apllicative / monad定律是非常好的。因为这是QuickCheck最有用的应用程序之一。

但是为Arbitrary编写StateT实例并非易事。这是可能的。但实际上它没有任何利润。您应该以某种聪明的方式使用CoArbitrary类型类。还有几个扩展。这个想法在this blog post中描述。拥有CoArbitrary a -> b个实例后,您可以轻松为Arbitrary创建StateT个实例。

instance ( CoArbitrary s
         , Arbitrary s
         , Arbitrary a
         , Arbitrary (m a)
         , Monad m
         ) => Arbitrary (StateT s m a)
  where
    arbitrary = StateT <$> promote (\s -> fmap (,s) <$> arbitrary)

然后你可以生成状态:

ghci> (`runStateT` 3) <$> generate (arbitrary @(StateT Int Maybe Bool))
Just (True,3)

你甚至可以写属性:

propStateFunctorId :: forall m s a .
                      ( Arbitrary s
                      , Eq (m (a, s))
                      , Show s
                      , Show (m (a, s))
                      , Functor m
                      )
                   => StateT s m a -> Property
propStateFunctorId st = forAll arbitrary $ \s -> 
                            runStateT (fmap id st) s === runStateT st s

但是您无法运行此属性,因为instance Show需要StateT,并且您无法为其编写合理的实例:(它只是{{{{它应该打印失败的反例。如果你不知道哪一个失败,那么知道100个测试通过和1个测试失败的事实并没有真正帮助你。这不是一个编程竞赛:)

QuickCheck

因此,生成ghci> quickCheck (propStateFunctorId @Maybe @Int @Bool) <interactive>:68:1: error: • No instance for (Show (StateT Int Maybe Bool)) arising from a use of ‘quickCheck’ StateT然后检查属性,而不是生成任意s

a

这种方法并不需要掌握一些高级技能。