2017-05-23 3 views
2

Je possède ce type qui enveloppe une fonctionComment écrire une instance arbitraire pour un type qui enveloppe une fonction?

newtype Transaction d e r = Transaction (d -> Either e (d,r)) 

... et je veux faire des tests pour ses QuickCheck Functor & instances Applicative mais le compilateur se plaint qu'il n'a pas une instance arbitraire.

J'ai essayé de le faire, mais je suis bloqué à générer la fonction aléatoire.

Merci!

== == Mise à jour

Les propriétés sont définies comme QuickCheck cette

type IdProperty f a = f a -> Bool 
functorIdProp :: (Functor f, Eq (f a)) => IdProperty f a 
functorIdProp x = (fmap id x) == id x 

type CompositionProperty f a b c = f a -> Fun a b -> Fun b c -> Bool 
functorCompProp :: (Functor f, Eq (f c)) => CompositionProperty f a b c 
functorCompProp x (apply -> f) (apply -> g) = (fmap (g . f) x) == (fmap g . fmap f $ x) 

instance (Arbitrary ((->) d (Either e (d, a)))) => Arbitrary (DbTr d e a) where 
    arbitrary = do 
     f <- ...??? 
     return $ Transaction f 

... et les essais ressembler à ceci:

spec = do 
    describe "Functor properties for (Transaction Int String)" $ do 
    it "IdProperty (Transaction Int String) Int" $ do 
     property (functorIdProp :: IdProperty (Transaction Int String) Int) 

    it "CompositionProperty (Transaction Int String) Int String Float" $ do 
     property (functorCompProp :: CompositionProperty (Transaction Int String) Int String Float) 
+0

[? Peut-être que ce sera utile] (https://mail.haskell.org/pipermail/haskell- cafe/2010-September/083735.html) Je penserais à ce que vous allez vraiment gagner de ces fonctions aléatoires, cependant. Je dirais que certains cas de bord est tout ce dont cela a besoin. –

Répondre

3

Vous devez utiliser Test.QuickCheck.Function wrapper pour fonctions de test. Il semble n'avoir aucun sens d'avoir Arbitrary ou CoArbitrary instances pour Transaction si vous avez seulement besoin de tester les lois de classe de type pour votre type Transaction (mais si vous en avez vraiment besoin, vous pouvez trouver my answer to this question).

Pour tester les lois que vous pouvez écrire des propriétés de la manière comme ceci:

{-# LANGUAGE DeriveFunctor #-} 

import Test.QuickCheck 
import Test.QuickCheck.Function 

newtype Transaction d e r = Transaction (d -> Either e (d,r)) 
    deriving (Functor) 

-- fmap id ≡ id 
prop_transactionFunctorId :: Int 
          -> Fun Int (Either Bool (Int,String)) 
          -> Bool 
prop_transactionFunctorId d (Fun _ f) = let t    = Transaction f 
              Transaction f' = fmap id t 
             in f' d == f d 

Eh bien, cela peut ressembler à pas aussi jolie et agréable que vous voulez. Mais c'est un bon moyen de tester des fonctions arbitraires. Par exemple, on peut remplacer la dernière ligne in f' d == f d à in f' d == f 1 pour voir comment il échoue si elle échoue:

ghci> quickCheck prop_transactionFunctorId 
*** Failed! Falsifiable (after 2 tests and 4 shrinks):  
0 
{0->Left False, _->Right (0,"")} 
+0

Je vois cela, mais je voudrais définir les propriétés Functor d'une manière générique. Voir mon code ci-dessus. De cette façon, je n'aurai pas à réécrire les propriétés pour chaque type que j'écris – vidi

+0

Le lien vers la question StateT est bon. Merci! – vidi