{-# LANGUAGE FlexibleContexts, RankNTypes, ScopedTypeVariables, TypeApplications #-}
module Writer
( tests
, gen0
, genN
, test
) where
import Control.Arrow ((&&&))
import qualified Control.Carrier.Writer.Strict as WriterC
import Control.Effect.Writer
import qualified Control.Monad.Trans.RWS.Lazy as LazyRWST
import qualified Control.Monad.Trans.RWS.Strict as StrictRWST
import qualified Control.Monad.Trans.Writer.Lazy as LazyWriterT
import qualified Control.Monad.Trans.Writer.Strict as StrictWriterT
import Data.Bifunctor (first)
import Data.Tuple (swap)
import Gen
import qualified Monad
import qualified MonadFix
import Test.Tasty
import Test.Tasty.Hedgehog
tests :: TestTree
tests = testGroup "Writer"
[ testGroup "WriterC" $
[ testMonad
, testMonadFix
, testWriter
] >>= ($ runL WriterC.runWriter)
, testGroup "(,)" $ testWriter (runL pure)
, testGroup "WriterT (Lazy)" $ testWriter (runL (fmap swap . LazyWriterT.runWriterT))
, testGroup "WriterT (Strict)" $ testWriter (runL (fmap swap . StrictWriterT.runWriterT))
, testGroup "RWST (Lazy)" $ testWriter (runL (runRWST LazyRWST.runRWST))
, testGroup "RWST (Strict)" $ testWriter (runL (runRWST StrictRWST.runRWST))
] where
testMonad run = Monad.test (m (gen0 w) (genN w b)) a b c initial run
testMonadFix run = MonadFix.test (m (gen0 w) (genN w b)) a b initial run
testWriter run = Writer.test w (m (gen0 w) (genN w b)) a initial run
initial = identity <*> unit
runRWST f m = (\ (a, _, w) -> (w, a)) <$> f m () ()
gen0 :: Has (Writer w) sig m => GenTerm w -> GenTerm a -> [GenTerm (m a)]
gen0 w a = [ infixL 4 "<$" (<$) <*> a <*> (label "tell" tell <*> w) ]
genN
:: forall w b m a sig
. (Has (Writer w) sig m, Arg b, Arg w, Show b, Show w, Vary b, Vary w)
=> GenTerm w
-> GenTerm b
-> GenM m
-> GenTerm a
-> [GenTerm (m a)]
genN w b m a =
[ atom "fmap" fmap <*> fn a <*> (label "listen" (listen @w) <*> m b)
, subtermM (m a) (label "censor" censor <*> fn w <*>)
]
test
:: (Has (Writer w) sig m, Arg w, Eq a, Eq w, Monoid w, Show a, Show w, Vary w, Functor f)
=> GenTerm w
-> GenM m
-> GenTerm a
-> GenTerm (f ())
-> Run f ((,) w) m
-> [TestTree]
test w m a i (Run runWriter) =
[ testProperty "tell appends a value to the log" . forall (i :. w :. m a :. Nil) $
\ i w m -> runWriter ((tell w >> m) <$ i) === fmap (first (mappend w)) (runWriter (m <$ i))
, testProperty "listen eavesdrops on written output" . forall (i :. m a :. Nil) $
\ i m -> runWriter (listen m <$ i) === fmap (fst &&& id) (runWriter (m <$ i))
, testProperty "censor revises written output" . forall (i :. fn w :. m a :. Nil) $
\ i f m -> runWriter (censor f m <$ i) === fmap (first f) (runWriter (m <$ i))
]