2015-07-29 1 views
18

In Testing Monadic Code with QuickCheck (Claessen, Hughes 2002) hat assert den Typ:Test.QuickCheck.Monadic: warum Bool assert angewendet wird, nicht testbare a => a

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

jedoch in Test.QuickCheck.Monadic, hat es die Art:

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

Warum hat assert den letzteren Typ in der Bibliothek?

+1

Vielleicht helfen die Kommentare in der Quelle: [(link)] (https://github.com/nick8325/quickcheck/blob/master/Test/QuickCheck/Monadic.hs#L118-126). Interessant, dass der Kommentar für 'assert' das sig des Papiers hat und auch' stop' grundsätzlich dasselbe sig hat. – ErikR

Antwort

4

Ich denke, es zu technischen Zwänge zurückzuführen ist, weil derzeit ein Testable mit der Test.QuickCheck Bibliothek zu bewerten, müssen Sie eine der quickCheck* Funktionen verwenden, die sehr IO -centric. Das passiert, weil QuickCheck die Eigenschaften Testable durch zufällige Generierung möglicher Eingaben (standardmäßig 100) testet und versucht, einen counterexample zu finden, der die Eigenschaft false beweist. Wenn eine solche Eingabe nicht gefunden wird, wird angenommen, dass die Eigenschaft wahr ist (obwohl dies nicht notwendigerweise die Wahrheit ist; es kann ein Gegenbeispiel geben, das nicht getestet wurde). Und um zufällige Eingaben in Haskell erzeugen zu können, bleiben wir bei der IO Monade.

Beachten Sie, dass, obwohl assert in einer solchen generischen Weise definiert wurde, es durch das gesamte Papier nur mit Bool verwendet wird. So der Bibliotheksautor (der gleiche der Zeitung) bevorzugt, den generischen Testable Parameter für einen einfachen Bool zu opfern, um keine Monade an diesem Punkt zu erzwingen.

Und wir können sehen, dass sie sogar die Original-Unterschrift als Kommentar in der source code geschrieben:

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

Beachten Sie auch, dass trotz der Tatsache, dass stop Funktion eine ähnliche Signatur hat:

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

Es ist nicht das gleiche wie die assert Funktion in der Zeitung, als die ehemalige stoppen die Berechnung in b In beiden Fällen ist die Bedingung True oder False. Auf der anderen Seite wird assert nur die Berechnung stoppen, wenn die Bedingung False ist:

⟦behaupten Wahr »p⟧ = ⟦p⟧

⟦behaupten Falsch» p⟧ = {Falsch zurück}

obwohl leicht eine IO Version der assert Funktion aus dem Papier schreiben

Wir können:

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" 

Und jetzt können wir einen Test machen die Unterschiede zwischen assertIO und stop zu sehen:

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 

Die succeeded und failed konnte durch True und False bzw. ersetzt werden. Es war nur zu zeigen, dass wir jetzt nicht auf Bool beschränkt sind, stattdessen können wir jedes Testable verwenden.

Und der Ausgang ist:

prop_assert:
*** Failed! Assertion fehlgeschlagen (nach 1 Test):
prop_stop:
+++ OK, bestanden 100 Tests.

Wie wir, trotz der Tatsache, dass die erste assertIO gelungen sehen können, scheiterte prop_assert aufgrund der zweiten assertIO. Auf der anderen Seite, prop_stop bestanden den Test, weil die erste stop erfolgreich war und die Berechnung zu diesem Zeitpunkt gestoppt wurde, nicht die zweite stop testen.

+0

Aber was ist der Grund, dass die 'quickCheck *' -Funktionen "sehr IO-centric" sind, wenn es dazu führt, etwas von der Allgemeingültigkeit in der Bibliothek aufzugeben, wie von der Zeitung vorgeschlagen. – frasertweedale

+0

@frasertweedale Das Problem ist, dass QuickCheck 'Testable' Eigenschaften durch zufällige Generierung von möglichen Eingaben (standardmäßig 100) testet und versucht, ein [Gegenbeispiel] (https://en.wikipedia.org/wiki/Counterexample) zu finden, das die Eigenschaft beweist falsch. Wenn eine solche Eingabe nicht gefunden wird, wird die Eigenschaft als wahr angenommen (obwohl dies nicht unbedingt die Wahrheit ist; es kann ein Gegenbeispiel geben, das nicht getestet wurde). Und um in Haskell zufällige Eingaben generieren zu können, bleiben wir bei der IO-Monade. Beachten Sie auch, dass, selbst wenn 'assert' auf eine generische Weise definiert wurde, es durch das gesamte Papier nur mit' Bool' verwendet wird. –

+0

Ok, es fängt an, ein bisschen klarer zu werden .. die obigen Punkte wären gut, um sie in deine Antwort aufzunehmen. – frasertweedale