{- HLINT ignore "Redundant reverse" -}
module Main where
import Effectful
import Effectful.Error.Static (runError, throwError)
import Effectful.Prim (Prim, runPrim)
import Effectful.Prim.IORef (modifyIORef', newIORef, readIORef)
import Effectful.QuickCheck
import Effectful.Reader.Static (Reader, ask, runReader)
import Prelude
prop_reverse :: [Int] -> Bool
prop_reverse xs = (reverse . reverse) xs == xs
prop_readerScaling :: (Reader Int :> es) => Int -> Eff es Bool
prop_readerScaling x = do
factor <- ask
pure (x * factor == factor * x)
prop_errorRecovers :: Int -> Eff es (Property es)
prop_errorRecovers n = do
result <- runError @String (if odd n then throwError ("odd" :: String) else pure n)
pure $ either (const $ property (odd n)) (=== n) result
prop_iorefAccumulates :: (Prim :> es) => [Int] -> Eff es (Property es)
prop_iorefAccumulates xs = do
ref <- newIORef (0 :: Int)
mapM_ (\x -> modifyIORef' ref (+ x)) xs
acc <- readIORef ref
pure $ counterexample ("running total was " <> show acc) (acc === sum xs)
main :: IO ()
main = runEff . runPrim . runReader (7 :: Int) $ do
-- Pure props
quickCheck prop_reverse
quickCheck $ withMaxSuccess 10_000 prop_reverse
-- Effectful props
quickCheck prop_readerScaling
quickCheck prop_errorRecovers
quickCheck prop_iorefAccumulates
-- Composition
quickCheck $ withMaxSuccess 500 prop_readerScaling
quickCheck $ prop_readerScaling .&&. prop_iorefAccumulates
quickCheck $ prop_reverse .||. prop_readerScaling
quickCheck . forAll (chooseInt (0, 100)) $ \x ->
counterexample ("x = " <> show x) (prop_readerScaling x)
quickCheck $ \x -> x > 0 ==> prop_readerScaling x
quickCheck $ classify True "always" . label "reader" $ prop_readerScaling
quickCheck . conjoin $ [prop_readerScaling, prop_readerScaling]
quickCheck $ once (prop_reverse [])