2015-07-29 15 views
18

In Testing Monadic Code with QuickCheck (Claessen, Hughes 2002), assert ha il tipo:Test.QuickCheck.Monadic: perché è assert applicato a Bool, non testabile a => un

assert :: (Monad m, Testable a) => a -> PropertyM m() 

Tuttavia, in Test.QuickCheck.Monadic, ha il tipo:

assert :: (Monad m) => Bool -> PropertyM m() 

Perché lo assert ha il secondo tipo nella libreria?

+1

Forse i commenti nella fonte saranno di qualche aiuto: [(collegamento)] (https://github.com/nick8325/quickcheck/blob/master/Test/QuickCheck/Monadic.hs#L118-126). Interessante che il commento per "assert" abbia il sig della carta, e anche "stop" abbia fondamentalmente lo stesso sig. – ErikR

risposta

4

Penso che sia stato a causa di vincoli tecnici perché, al momento di valutare una Testable con la libreria Test.QuickCheck, è necessario utilizzare una delle quickCheck* funzioni, che sono molto IO -centric. Ciò accade perché QuickCheck verifica le proprietà Testable generando casualmente gli input possibili (per impostazione predefinita 100), cercando di trovare uno counterexample che provi la proprietà false. Se tale input non viene trovato, si presume che la proprietà sia vera (sebbene ciò non sia necessariamente la verità, potrebbe esserci un controesempio che non è stato testato). E per essere in grado di generare input casuali in Haskell, siamo attaccati alla monade IO.

Si noti che anche se assert è stato definito in modo generico, viene utilizzato attraverso tutta la carta solo con Bool. Quindi l'autore della libreria (lo stesso della carta) ha preferito sacrificare il parametro generico Testable per un semplice Bool, per non forzare alcuna monade a questo punto.

E possiamo vedere che essi hanno persino scritto la firma originale come commentare nel source code:

-- assert :: Testable prop => prop -> PropertyM m() 

Si noti inoltre che nonostante il fatto che stop funzione ha una firma simile:

stop :: (Testable prop, Monad m) => prop -> PropertyM m a 

È non uguale alla funzione assert nella carta, poiché il primo sarà interrompe il calcolo in b in altri casi, la condizione è True o False. D'altra parte, assert si fermerà solo il calcolo se la condizione è False:

⟦affermare vero »p⟧ = ⟦p⟧

⟦affermare Falso» p⟧ = {return False}

possiamo però facilmente scrivere una versione IO della funzione assert dalla carta:

import Control.Monad 
import Control.Monad.Trans 
import Test.QuickCheck 
import Test.QuickCheck.Monadic 
import Test.QuickCheck.Property 
import Test.QuickCheck.Test 

assertIO :: Testable prop => prop -> PropertyM IO() 
assertIO p = do r <- liftIO $ quickCheckWithResult stdArgs{chatty = False} p 
       unless (isSuccess r) $ fail "Assertion failed" 

E ora possiamo fare un test per vedere le differenze tra assertIO e stop:

prop_assert :: Property 
prop_assert = monadicIO $ do assertIO succeeded 
          assertIO failed 

prop_stop :: Property 
prop_stop = monadicIO $ do stop succeeded 
          stop failed 

main :: IO() 
main = do putStrLn "prop_assert:" 
      quickCheck prop_assert 
      putStrLn "prop_stop:" 
      quickCheck prop_stop 

Il succeeded e failed potrebbe essere sostituito rispettivamente True e False,. Era solo per dimostrare che ora non siamo limitati a Bool, invece possiamo usare qualsiasi Testable.

e l'uscita è:

prop_assert:
*** non riuscita! Asserzione non riuscita (dopo 1 test):
prop_stop:
+++ OK, superato 100 test.

Come possiamo vedere, nonostante il fatto che il primo assertIO riuscito, prop_assert non riuscito a causa della seconda assertIO. D'altra parte, prop_stop ha superato il test, perché il primo stop è riuscito e il calcolo si è fermato a quel punto, non testando il secondo stop.

+0

Ma quale è la ragione per cui le funzioni 'quickCheck *' "sono molto incentrate sull'IO", se porta a rinunciare a parte della generalità nella libreria come proposto dal documento. – frasertweedale

+0

@frasertweedale Il problema è che QuickCheck testa le proprietà 'Testabili' generando casualmente gli input possibili (di default 100), cercando di trovare un [controesempio] (https://en.wikipedia.org/wiki/Counterexample) che dimostra la proprietà falsa. Se tale input non viene trovato, la proprietà viene presupposta vera (sebbene ciò non sia necessariamente la verità, potrebbe esserci un controesempio che non è stato testato). E per essere in grado di generare input casuali in Haskell, siamo attaccati alla monade 'IO'. Si noti inoltre che anche se 'assert' è stato definito in modo generico, viene utilizzato su tutta la carta solo con' Bool'. –

+0

Ok, sta iniziando a diventare un po 'più chiaro .. i punti di cui sopra sarebbero buoni da includere nella risposta. – frasertweedale