{-# 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 :: (Arbitrary s, Member (State s) r, Eq x, Show x) => (Sem r s -> IO x) -> Property putPutLaw = prepropLaw $ do s1 <- arbitrary s2 <- arbitrary pure ( do put s1 put s2 get , do put s2 get ) getPutLaw :: (Arbitrary s, Member (State s) r, Eq x, Show x) => (Sem r () -> IO x) -> Property getPutLaw = prepropLaw $ do pure ( get >>= put , pure () ) putGetLaw :: (Arbitrary s, Member (State s) r, Eq x, Show x) => (Sem r s -> IO x) -> Property putGetLaw = prepropLaw $ 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)