你的问题真的很有趣。事实上,使用QuickCheck
来验证函数/定律/单子定律对于单子变压器StateT
将是非常好的。因为这是QuickCheck
最有用的应用之一。
但是写Arbitrary
实例为StateT
并不重要。这是可能的。但实际上它没有利润。您应该以某种智能方式使用CoArbitrary
类型类。还有几个扩展。该想法在this blog post中描述。对于a -> b
,有CoArbitrary
实例,您可以轻松地为StateT
创建Arbitrary
实例。
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
,你不能运行这个属性不能写出明智的实例:(这只是QuickCheck
的工作原理,它应该打印失败的反例,了解100 te如果你不知道哪一个失败了,那么sts通过并且一次测试失败并不能真正帮助你。这不是一个编程大赛:)
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
,然后检查性能。
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.
这种方法不需要一些高级技能的知识。
......听起来像是一个XY问题。 – Zeta
@Zeta我认为每个人都可以搜索解决方案,因为StateT在基础库中......但重点是,我想测试它 – pterodragon
我不是在谈论*“重新发明车轮”*(由方式,'StateT'不在'base'中)。我在谈论你[询问“X”(如何写一个Arbitarry实例),但实际上只是想要“Y”(如何测试我的应用程序的StateT实例)](https://meta.stackexchange.com/问题/ 66377 /什么,是最XY-问题)。 – Zeta