packages feed

freer-simple-catching-0.1.0.0: test/Control/Monad/Freer/CatchingSpec.hs

module Control.Monad.Freer.CatchingSpec where

import           Control.Monad
import           Control.Exception
import           Control.Monad.Freer
import           Control.Monad.Freer.Catching
import           Test.Hspec

data Div a where
  Div :: Int -> Int -> Div Int

runDiv :: forall r v . Eff (Div ': r) v -> Eff r v
runDiv = interpret handler
  where
    handler :: Div a -> Eff r a
    handler (Div x y) = return $ x `div` y

data Test a where
  Succeed :: Test Int
  Fail :: Test Bool

data TestException = TestException deriving (Show, Exception, Eq)

runTest :: forall r v . (Member IO r)
        => Eff (Test ': r) v
       -> Eff r v
runTest = interpret handler
  where
    handler :: Test a -> Eff r a
    handler Succeed = return 3
    handler Fail = throw TestException

spec :: Spec
spec = do
  describe "div" $ do
    it "divides" $ do
      (run . runDiv . send $ Div 4 2) `shouldBe` 2

  describe "catching" $ do
    it "lets unexceptional values through from pure code" $ do
      result <- runM . runCatching @ArithException runDiv $
        catching @ArithException @Div (send $ Div 2 1)
      result `shouldBe` Right 2

    it "catches exceptions from pure code" $ do
      result <- runM . runCatching @ArithException runDiv $
        catching @ArithException @Div (send $ Div 2 0)
      result `shouldBe` Left DivideByZero

    it "lets unexceptional io values through" $ do
      result <- runM . runCatching @TestException runTest $
        catching @TestException @Test (send Succeed)
      result `shouldBe` Right 3

    it "catches io exceptions" $ do
      result <- runM . runCatching @TestException runTest $
        catching @TestException @Test (send Fail)
      result `shouldBe` Left TestException

    it "catches inside forM" $ do
      result <- runM . runCatching @TestException runTest $
        forM [1,2,3] $ \i -> do
          x <- catching @TestException @Test (send Fail)
          y <- case x of
            Left e -> return i
            Right v -> if v then return 1 else return 2
          z <- send Succeed
          return $ y + z
      result `shouldBe` [4,5,6]