effectful-2.7.1.0: tests/ReturnWithTests.hs
module ReturnWithTests (returnWithTests) where
import Test.Tasty
import Test.Tasty.HUnit
import Effectful
import Effectful.Dispatch.Dynamic
import Effectful.Exception (finally)
import Effectful.Labeled.ReturnWith qualified as LR
import Effectful.ReturnWith.Dynamic qualified as RD
import Effectful.ReturnWith.Static qualified as RS
import Effectful.State.Static.Local
import Utils qualified as U
returnWithTests :: TestTree
returnWithTests = testGroup "ReturnWith"
[ testCase "static short-circuits" test_static
, testCase "static falls through" test_staticFallThrough
, testCase "different handlers are independent" test_independentHandlers
, testCase "cleanup actions run on early return" test_cleanup
, testCase "dynamic short-circuits" test_dynamic
, testCase "labeled handlers are targeted correctly" test_labeled
]
test_static :: Assertion
test_static = runEff . evalState @Int 0 $ do
r <- RS.runReturnWith @String $ do
modify @Int (+1)
_ <- RS.returnWith "early"
modify @Int (+1)
pure "late"
U.assertEqual "result" "early" r
U.assertEqual "state changes before returnWith persist" 1 =<< get @Int
test_staticFallThrough :: Assertion
test_staticFallThrough = runEff $ do
r <- RS.runReturnWith @String $ pure "done"
U.assertEqual "result" "done" r
test_independentHandlers :: Assertion
test_independentHandlers = runEff $ do
r <- RS.runReturnWith @String . runOuterReturn $ do
inner <- RS.runReturnWith @String $ do
outerReturn
pure "inner"
pure $ "inner handler caught " ++ inner
U.assertEqual "correct value returned" "outer" r
test_cleanup :: Assertion
test_cleanup = runEff . evalState @Int 0 $ do
r <- RS.runReturnWith @String $ do
(RS.returnWith "early" >> pure "late") `finally` modify @Int (+1)
U.assertEqual "result" "early" r
U.assertEqual "cleanup ran" 1 =<< get @Int
test_dynamic :: Assertion
test_dynamic = runEff . evalState @Int 0 $ do
r <- RD.runReturnWith @String $ do
modify @Int (+1)
_ <- RD.returnWith "early"
modify @Int (+1)
pure "late"
U.assertEqual "result" "early" r
U.assertEqual "state changes before returnWith persist" 1 =<< get @Int
test_labeled :: Assertion
test_labeled = runEff $ do
r <- LR.runReturnWith @"outer" @String $ do
n <- LR.runReturnWith @"inner" @Int $ do
_ <- LR.returnWith @"outer" "outer wins"
pure 0
pure $ "inner returned " ++ show n
U.assertEqual "value caught by the outer handler" "outer wins" r
----------------------------------------
-- Helpers
data OuterReturn :: Effect where
OuterReturn :: OuterReturn m ()
type instance DispatchOf OuterReturn = Dynamic
outerReturn :: OuterReturn :> es => Eff es ()
outerReturn = send OuterReturn
runOuterReturn :: RS.ReturnWith String :> es => Eff (OuterReturn : es) a -> Eff es a
runOuterReturn = interpret_ $ \case
OuterReturn -> RS.returnWith "outer"