Votre question est vraiment intéressante. En effet, il serait très agréable de vérifier les lois fonctor/apllicative/monad en utilisant QuickCheck
pour le transformateur monade StateT
. Parce que c'est l'une des applications les plus utiles de QuickCheck
. Mais l'écriture Arbitrary
instance pour StateT
n'est pas triviale. C'est possible. Mais en réalité, il n'y a pas de profit. Vous devriez utiliser la classe de type CoArbitrary
de manière intelligente. Et aussi des extensions de couple. L'idée est décrite dans this blog post. Ayant CoArbitrary
instance pour a -> b
vous pouvez facilement créer Arbitrary
instance pour 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)
Ensuite, vous pouvez générer des états:
ghci> (`runStateT` 3) <$> generate (arbitrary @(StateT Int Maybe Bool))
Just (True,3)
Et vous pouvez même écrire propriété:
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
Mais vous ne pouvez pas exécuter cette propriété car elle nécessite instance Show
pour StateT
et vous Impossible d'écrire une instance sensée pour cela :(C'est comme ça que fonctionne QuickCheck
.Il devrait imprimer un contre-exemple qui échoue.Connaissance du fait que 100 te sts ont été passés et 1 test échoué ne vous aide pas vraiment si vous ne savez pas lequel a échoué. Ce n'est pas un concours de programmation :)
ghci> quickCheck (propStateFunctorId @Maybe @Int @Bool)
<interactive>:68:1: error:
• No instance for (Show (StateT Int Maybe Bool))
arising from a use of ‘quickCheck’
Ainsi, au lieu de générer arbitraire StateT
il est préférable de générer s
et a
puis vérifier les propriétés.
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.
Cette approche n'exige pas la connaissance de certaines compétences de haut niveau.
Cela ... ressemble à un problème XY. – Zeta
@Zeta Je pense que tout le monde peut rechercher la solution comme StateT est dans la bibliothèque de base ... mais le point est, je veux le tester – pterodragon
Je ne parle pas de * "réinventer la roue" * (par le façon, 'StateT' ce n'est pas dans' base'). Je parle de vous [poser des questions sur "X" (comment écrire une instance Arbitarry) mais en fait juste vouloir "Y" (comment tester mon instance applicative de StateT)] (https://meta.stackexchange.com/ questions/66377/quoi-est-le-xy-problème). – Zeta