module ConcurrencySpec where import Cleff import Cleff.Error (runError, throwError) import Cleff.State import Control.Monad (void, when) import Data.Set (Set) import qualified Data.Set as Set import Test.Hspec import UnliftIO (concurrently_, replicateConcurrently_) import UnliftIO.Concurrent (threadDelay) spec :: Spec spec = do sharedState localState errorHandling sharedState :: Spec sharedState = it "should have shared state" do (_, set) <- runIOE $ runState (Set.empty @Int) do concurrently_ (addWhen even x) (addWhen odd x) set `shouldBe` Set.fromList [1..x] where x :: Int = 100 addWhen :: State (Set Int) :> es => (Int -> Bool) -> Int -> Eff es () addWhen f = \case 0 -> pure () n -> do when (f n) $ do modify $ Set.insert n addWhen f $ n - 1 localState :: Spec localState = it "should have local state" $ void $ runIOE $ runStateLocal x $ do replicateConcurrently_ 2 $ do r <- goDownward 0 liftIO $ r `shouldBe` x where x :: Int x = 100000 goDownward :: State Int :> es => Int -> Eff es Int goDownward acc = do end <- state @Int $ \case 0 -> (True, 0) n -> (False, n - 1) if end then pure acc else goDownward $ acc + 1 errorHandling :: Spec errorHandling = it "should handle errors properly" do (r, s) <- runIOE $ runState (0 :: Int) $ runError @String $ concurrently_ (liftIO (threadDelay 10000) >> throwError err) (modify (+x)) case r of Left e -> e `shouldBe` err Right _ -> expectationFailure "no error caught - or error escaped" s `shouldBe` x where x :: Int = 67 err = "error"