{-# OPTIONS_GHC -fplugin=Polysemy.Plugin #-} module LawSpec where import Polysemy import Polysemy.Check import Test.Hspec import Test.QuickCheck import Test.Hspec.QuickCheck import Polysemy.State import Data.IORef (newIORef, readIORef) spec :: Spec spec = do describe "pure state" $ do prop "put >> put = put" $ do s <- arbitrary pure $ putPutLaw Nothing $ runPureState s prop "get >>= put = pure ()" $ do s <- arbitrary pure $ getPutLaw Nothing $ runPureState s prop "put >> get = put >> pure" $ do s <- arbitrary pure $ putGetLaw Nothing $ runPureState s describe "io state" $ do prop "put >> put = put" $ do s <- arbitrary pure $ putPutLaw Nothing $ runIOState s prop "get >>= put = pure ()" $ do s <- arbitrary pure $ getPutLaw Nothing $ runIOState s prop "put >> get = put >> pure" $ do s <- arbitrary pure $ putGetLaw Nothing $ runIOState s type LawConstraints f effs s r = ( Arbitrary s , Members effs r , Eq s , Show s , ArbitraryEff effs r , f ~ (,) s , effs ~ '[State s] ) putPutLaw :: forall s r res effs f . ( res ~ () , LawConstraints f effs s r ) => Maybe (f res -> String) -> (forall a. Sem r (res, a) -> IO (f (res, a))) -> Property putPutLaw = prepropLaw @effs $ do s1 <- arbitrary s2 <- arbitrary pure $ simpleLaw ( do put s1 put s2) ( do put s2) getPutLaw :: forall s r res effs f . ( res ~ () , LawConstraints f effs s r ) => Maybe (f res -> String) -> (forall a. Sem r (res, a) -> IO (f (res, a))) -> Property getPutLaw = prepropLaw @effs $ do pure $ simpleLaw (get >>= put) (pure ()) putGetLaw :: forall s r res effs f . ( res ~ s , LawConstraints f effs s r ) => Maybe (f res -> String) -> (forall a. Sem r (res, a) -> IO (f (res, a))) -> Property putGetLaw = prepropLaw @effs $ do s <- arbitrary pure $ simpleLaw ( do put s get) ( do put s pure s) runPureState :: Int -> Sem '[State Int] a -> IO (Int, a) runPureState s = pure . run . runState s runIOState :: Int -> Sem '[State Int, Embed IO] a -> IO (Int, a) runIOState s sem = do ref <- newIORef s a <- runM . runStateIORef ref $ sem r <- readIORef ref pure (r, a)