首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用StateT编写QuickCheck测试

如何使用StateT编写QuickCheck测试
EN

Stack Overflow用户
提问于 2017-04-14 10:19:25
回答 1查看 349关注 0票数 2

StateT在Control.Monad.Trans.State.Lazy

内部的功能和m更高的类型使得它很难实现。

代码语言:javascript
复制
{-# 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的应用实例是否正确(用于实践)。

编辑:好的,下面是我要测试的实例(据说是不正确的)

代码语言:javascript
复制
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)
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-04-14 15:54:34

你的问题真的很有趣。实际上,使用QuickCheck来验证函子/apllicative/ monad律对于StateT单端转换器来说是非常好的。因为这是QuickCheck最有用的应用之一。

但是为Arbitrary编写StateT实例并不容易。这是可能的。但实际上是没有利润的。您应该以某种聪明的方式使用CoArbitrary类型类。还有几个扩展。这个想法在这篇博客文章中得到了描述。拥有CoArbitrary实例用于a -> b,您可以轻松地为StateT创建Arbitrary实例。

代码语言:javascript
复制
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)

然后,您可以生成状态:

代码语言:javascript
复制
ghci> (`runStateT` 3) <$> generate (arbitrary @(StateT Int Maybe Bool))
Just (True,3)

您甚至可以编写属性:

代码语言:javascript
复制
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,并且不能为它编写合理的实例:(这正是QuickCheck的工作方式。它应该打印失败的反例。如果你不知道哪一次考试失败了,那么100次考试通过,1次考试失败这一事实并不能真正帮助你。这不是编程竞赛:)

代码语言:javascript
复制
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,不如生成sa,然后检查属性。

代码语言:javascript
复制
prop_StateTFunctorId :: forall s a .
                      ( Arbitrary s
                      , Arbitrary a
                      , Eq a
                      , Eq s
                      )
                     => s -> a -> Bool
prop_StateTFunctorId s a = let st = pure a
                           in runStateT @_ @Maybe (fmap id st) s == runStateT st s

ghci> quickCheck (prop_StateTFunctorId @Int @Bool) 
+++ OK, passed 100 tests.

这种方法不需要掌握一些高水平的技能。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43409610

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档