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.
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