{-# 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 $ runPureState s prop "get >>= put = pure ()" $ do s <- arbitrary pure $ getPutLaw $ runPureState s prop "put >> get = put >> pure" $ do s <- arbitrary pure $ putGetLaw $ runPureState s describe "io state" $ do prop "put >> put = put" $ do s <- arbitrary pure $ putPutLaw $ runIOState s prop "get >>= put = pure ()" $ do s <- arbitrary pure $ getPutLaw $ runIOState s prop "put >> get = put >> pure" $ do s <- arbitrary pure $ putGetLaw $ runIOState s putPutLaw :: forall s r . ( Arbitrary s , Member (State s) r , Eq s , Show s , ArbitraryEff '[State s] r ) => (forall a. Sem r ((), a) -> IO (s, ((), a))) -> Property putPutLaw = prepropLaw @'[State s] $ do s1 <- arbitrary s2 <- arbitrary pure ( do put s1 put s2 , do put s2 ) getPutLaw :: forall s r . ( Arbitrary s , Member (State s) r , Eq s , Show s , ArbitraryEff '[State s] r ) => (forall a. Sem r ((), a) -> IO (s, ((), a))) -> Property getPutLaw = prepropLaw @'[State s] $ do pure ( get >>= put , pure () ) putGetLaw :: forall s r . ( Arbitrary s , Member (State s) r , Eq s , Show s , ArbitraryEff '[State s] r ) => (forall a. Sem r (s, a) -> IO (s, (s, a))) -> Property putGetLaw = prepropLaw @'[State s] $ do s <- arbitrary pure ( 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)