{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE DataKinds #-}

module Examples where

import Control.Monad.Except (MonadError, ExceptT, runExceptT)
import Control.Monad.Oops ( CouldBe, Variant )
import Data.Text (Text)
import Text.Read (readMaybe)
import Data.Functor.Identity (Identity)
import Control.Monad.Trans (MonadIO)
import Data.Function ((&))

import qualified Control.Monad.Oops as OO
import qualified System.IO as IO
import Control.Monad.IO.Class (MonadIO(..))
import Control.Exception (IOException)
import Control.Monad.Catch (MonadCatch)

-- | A simple function that throws an error.
--
-- The type is the one that is inferred by GHC.
readIntV1 :: ()
  => MonadError (Variant e) m
  => OO.CouldBeF e Text
  => String
  -> m Int
readIntV1 :: forall (e :: [*]) (m :: * -> *).
(MonadError (Variant e) m, CouldBeF e Text) =>
String -> m Int
readIntV1 String
s = case forall a. Read a => String -> Maybe a
readMaybe @Int String
s of
  Just Int
i -> forall (m :: * -> *) a. Monad m => a -> m a
return Int
i
  Maybe Int
Nothing -> forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
x -> m a
OO.throw @Text Text
"Not an integer"

-- | A simple function that throws an error.
--
-- This is the same as before, but we can rewrite constraint on 'e' differently.
readIntV2 :: ()
  => MonadError (Variant e) m
  => e `CouldBe` Text
  => String
  -> m Int
readIntV2 :: forall (e :: [*]) (m :: * -> *).
(MonadError (Variant e) m, CouldBe e Text) =>
String -> m Int
readIntV2 String
s = case forall a. Read a => String -> Maybe a
readMaybe @Int String
s of
  Just Int
i -> forall (m :: * -> *) a. Monad m => a -> m a
return Int
i
  Maybe Int
Nothing -> forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
x -> m a
OO.throw @Text Text
"Not an integer"

-- | A simple function that throws an error.
--
-- We can also use 'ExceptT'
readIntV3 :: ()
  => e `CouldBe` Text
  => String
  -> ExceptT (Variant e) Identity Int
readIntV3 :: forall (e :: [*]).
CouldBe e Text =>
String -> ExceptT (Variant e) Identity Int
readIntV3 String
s = case forall a. Read a => String -> Maybe a
readMaybe @Int String
s of
  Just Int
i -> forall (m :: * -> *) a. Monad m => a -> m a
return Int
i
  Maybe Int
Nothing -> forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
x -> m a
OO.throw @Text Text
"Not an integer"

-- | A simple IO function that throws an error.
--
-- We can also use 'ExceptT' of 'IO'.
readIntV4 :: ()
  => e `CouldBe` Text
  => String
  -> ExceptT (Variant e) IO Int
readIntV4 :: forall (e :: [*]).
CouldBe e Text =>
String -> ExceptT (Variant e) IO Int
readIntV4 String
s = case forall a. Read a => String -> Maybe a
readMaybe @Int String
s of
  Just Int
i -> forall (m :: * -> *) a. Monad m => a -> m a
return Int
i
  Maybe Int
Nothing -> forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
x -> m a
OO.throw @Text Text
"Not an integer"

-- | A simple function that throws an error.
--
-- Or use MonadIO instead of IO directly.
readIntV5 :: ()
  => MonadError (Variant e) m
  => MonadIO m
  => e `CouldBe` Text
  => String
  -> m Int
readIntV5 :: forall (e :: [*]) (m :: * -> *).
(MonadError (Variant e) m, MonadIO m, CouldBe e Text) =>
String -> m Int
readIntV5 String
s = case forall a. Read a => String -> Maybe a
readMaybe @Int String
s of
  Just Int
i -> forall (m :: * -> *) a. Monad m => a -> m a
return Int
i
  Maybe Int
Nothing -> forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
x -> m a
OO.throw @Text Text
"Not an integer"

-- | A simple function that throws an error.
--
-- Or use 'MonadIO' with 'ExceptT'.
readIntV6 :: ()
  => MonadIO m
  => e `CouldBe` Text
  => String
  -> ExceptT (Variant e) m Int
readIntV6 :: forall (m :: * -> *) (e :: [*]).
(MonadIO m, CouldBe e Text) =>
String -> ExceptT (Variant e) m Int
readIntV6 String
s = case forall a. Read a => String -> Maybe a
readMaybe @Int String
s of
  Just Int
i -> forall (m :: * -> *) a. Monad m => a -> m a
return Int
i
  Maybe Int
Nothing -> forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
x -> m a
OO.throw @Text Text
"Not an integer"

-- We can represent each error as a separate type.

data NotAnInteger = NotAnInteger

data NotPositive = NotPositive

-- | A simple function can throw two errors
readPositiveInt1 :: ()
  => MonadIO m
  => e `CouldBe` NotAnInteger
  => e `CouldBe` NotPositive
  => String
  -> ExceptT (Variant e) m Int
readPositiveInt1 :: forall (m :: * -> *) (e :: [*]).
(MonadIO m, CouldBe e NotAnInteger, CouldBe e NotPositive) =>
String -> ExceptT (Variant e) m Int
readPositiveInt1 String
s =
  case forall a. Read a => String -> Maybe a
readMaybe @Int String
s of
    Just Int
i ->
      if Int
i forall a. Ord a => a -> a -> Bool
> Int
0
        then forall (m :: * -> *) a. Monad m => a -> m a
return Int
i
        else forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
x -> m a
OO.throw NotPositive
NotPositive
    Maybe Int
Nothing -> forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
x -> m a
OO.throw NotAnInteger
NotAnInteger

-- We can call a function that throws an error of type 'Text' and allow the
-- error to propagate by declaring we also throw an error of type 'Text'.
example1 :: ()
  => e `CouldBe` Text
  => String
  -> ExceptT (Variant e) IO Int
example1 :: forall (e :: [*]).
CouldBe e Text =>
String -> ExceptT (Variant e) IO Int
example1 String
s = do
  Int
i <- forall (e :: [*]).
CouldBe e Text =>
String -> ExceptT (Variant e) IO Int
readInt String
s
  forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> IO ()
IO.print Int
i
  forall (m :: * -> *) a. Monad m => a -> m a
return Int
i
  where
    readInt :: ()
      => e `CouldBe` Text
      => String
      -> ExceptT (Variant e) IO Int
    readInt :: forall (e :: [*]).
CouldBe e Text =>
String -> ExceptT (Variant e) IO Int
readInt = forall a. HasCallStack => String -> a
error String
"unimplemented"

-- Or alternatively, we can catch the error of type 'Text' and handle it.
-- in which case the error doesn't propagate.  Notice the 'e CouldBe Text'
-- constraint is not needed in this case.
example2 :: ()
  => String
  -> ExceptT (Variant e) IO Int
example2 :: forall (e :: [*]). String -> ExceptT (Variant e) IO Int
example2 String
s = do
  Int
i <- forall (e :: [*]).
CouldBe e Text =>
String -> ExceptT (Variant e) IO Int
readInt String
s
    forall a b. a -> (a -> b) -> b
& forall x (e :: [*]) (e' :: [*]) (m :: * -> *) a.
(Monad m, Catch x e e') =>
(x -> ExceptT (Variant e') m a)
-> ExceptT (Variant e) m a -> ExceptT (Variant e') m a
OO.catch @Text (\Text
_ -> forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
0)
  forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> IO ()
IO.print Int
i
  forall (m :: * -> *) a. Monad m => a -> m a
return Int
i
  where
    readInt :: ()
      => e `CouldBe` Text
      => String
      -> ExceptT (Variant e) IO Int
    readInt :: forall (e :: [*]).
CouldBe e Text =>
String -> ExceptT (Variant e) IO Int
readInt = forall a. HasCallStack => String -> a
error String
"unimplemented"

-- When we don't throw any errors, we can use the 'runOops' function to
-- convert the 'ExceptT' to an 'IO' action.
example3 :: ()
  => String
  -> IO Int
example3 :: String -> IO Int
example3 String
s = forall (m :: * -> *) a. Monad m => ExceptT (Variant '[]) m a -> m a
OO.runOops forall a b. (a -> b) -> a -> b
$ do
  Int
i <- forall (e :: [*]).
CouldBe e Text =>
String -> ExceptT (Variant e) IO Int
readInt String
s
    forall a b. a -> (a -> b) -> b
& forall x (e :: [*]) (e' :: [*]) (m :: * -> *) a.
(Monad m, Catch x e e') =>
(x -> ExceptT (Variant e') m a)
-> ExceptT (Variant e) m a -> ExceptT (Variant e') m a
OO.catch @Text (\Text
_ -> forall (f :: * -> *) a. Applicative f => a -> f a
pure Int
0)
  forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> IO ()
IO.print Int
i
  forall (m :: * -> *) a. Monad m => a -> m a
return Int
i
  where
    readInt :: ()
      => e `CouldBe` Text
      => String
      -> ExceptT (Variant e) IO Int
    readInt :: forall (e :: [*]).
CouldBe e Text =>
String -> ExceptT (Variant e) IO Int
readInt = forall a. HasCallStack => String -> a
error String
"unimplemented"

data FileNotFound = FileNotFound

data FileNotReadable = FileNotReadable

data Errors1
  = Errors1NotPositive NotPositive
  | Errors1NotAnInteger NotAnInteger

-- We can call a function that throws multiple errors into a function
-- that only throws one by just catching and rethrowing the one
-- error.
example4 :: ()
  => MonadIO m
  => e `CouldBe` Errors1
  => String
  -> ExceptT (Variant e) m Int
example4 :: forall (m :: * -> *) (e :: [*]).
(MonadIO m, CouldBe e Errors1) =>
String -> ExceptT (Variant e) m Int
example4 String
s = do
  Int
i <- forall (m :: * -> *) (e :: [*]).
(MonadIO m, CouldBe e NotAnInteger, CouldBe e NotPositive) =>
String -> ExceptT (Variant e) m Int
readPositiveInt1 String
s
    forall a b. a -> (a -> b) -> b
& forall x (e :: [*]) (e' :: [*]) (m :: * -> *) a.
(Monad m, Catch x e e') =>
(x -> ExceptT (Variant e') m a)
-> ExceptT (Variant e) m a -> ExceptT (Variant e') m a
OO.catch @NotPositive (forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
x -> m a
OO.throw forall b c a. (b -> c) -> (a -> b) -> a -> c
. NotPositive -> Errors1
Errors1NotPositive)
    forall a b. a -> (a -> b) -> b
& forall x (e :: [*]) (e' :: [*]) (m :: * -> *) a.
(Monad m, Catch x e e') =>
(x -> ExceptT (Variant e') m a)
-> ExceptT (Variant e) m a -> ExceptT (Variant e') m a
OO.catch @NotAnInteger (forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
x -> m a
OO.throw forall b c a. (b -> c) -> (a -> b) -> a -> c
. NotAnInteger -> Errors1
Errors1NotAnInteger)
  forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> IO ()
IO.print Int
i
  forall (m :: * -> *) a. Monad m => a -> m a
return Int
i

--------------------------------------------------------------------------------
-- Embedding 'Oops' into vanilla 'ExceptT' and 'Either' code.

-- We we have a function that only throws one error, we can use 'runOopsInExceptT'
-- to remove the 'Variant' wrapper leaving only the 'ExceptT'.
example5 :: ()
  => MonadIO m
  => String
  -> ExceptT Errors1 m Int
example5 :: forall (m :: * -> *). MonadIO m => String -> ExceptT Errors1 m Int
example5 String
s = forall x (m :: * -> *) a.
Monad m =>
ExceptT (Variant '[x]) m a -> ExceptT x m a
OO.runOopsInExceptT forall a b. (a -> b) -> a -> b
$ do
  Int
i <- forall (m :: * -> *) (e :: [*]).
(MonadIO m, CouldBe e NotAnInteger, CouldBe e NotPositive) =>
String -> ExceptT (Variant e) m Int
readPositiveInt1 String
s
    forall a b. a -> (a -> b) -> b
& forall x (e :: [*]) (e' :: [*]) (m :: * -> *) a.
(Monad m, Catch x e e') =>
(x -> ExceptT (Variant e') m a)
-> ExceptT (Variant e) m a -> ExceptT (Variant e') m a
OO.catch @NotPositive (forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
x -> m a
OO.throw forall b c a. (b -> c) -> (a -> b) -> a -> c
. NotPositive -> Errors1
Errors1NotPositive)
    forall a b. a -> (a -> b) -> b
& forall x (e :: [*]) (e' :: [*]) (m :: * -> *) a.
(Monad m, Catch x e e') =>
(x -> ExceptT (Variant e') m a)
-> ExceptT (Variant e) m a -> ExceptT (Variant e') m a
OO.catch @NotAnInteger (forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
x -> m a
OO.throw forall b c a. (b -> c) -> (a -> b) -> a -> c
. NotAnInteger -> Errors1
Errors1NotAnInteger)
  forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> IO ()
IO.print Int
i
  forall (m :: * -> *) a. Monad m => a -> m a
return Int
i

-- We we have a function that only throws one error, we can use 'runOopsInEither'
-- to remove the 'Variant' wrapper and the 'Except' leaving only the 'Either'.
example6 :: ()
  => MonadIO m
  => String
  -> m (Either Errors1 Int)
example6 :: forall (m :: * -> *). MonadIO m => String -> m (Either Errors1 Int)
example6 String
s = forall x (m :: * -> *) a.
Monad m =>
ExceptT (Variant '[x]) m a -> m (Either x a)
OO.runOopsInEither forall a b. (a -> b) -> a -> b
$ do
  Int
i <- forall (m :: * -> *) (e :: [*]).
(MonadIO m, CouldBe e NotAnInteger, CouldBe e NotPositive) =>
String -> ExceptT (Variant e) m Int
readPositiveInt1 String
s
    forall a b. a -> (a -> b) -> b
& forall x (e :: [*]) (e' :: [*]) (m :: * -> *) a.
(Monad m, Catch x e e') =>
(x -> ExceptT (Variant e') m a)
-> ExceptT (Variant e) m a -> ExceptT (Variant e') m a
OO.catch @NotPositive (forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
x -> m a
OO.throw forall b c a. (b -> c) -> (a -> b) -> a -> c
. NotPositive -> Errors1
Errors1NotPositive)
    forall a b. a -> (a -> b) -> b
& forall x (e :: [*]) (e' :: [*]) (m :: * -> *) a.
(Monad m, Catch x e e') =>
(x -> ExceptT (Variant e') m a)
-> ExceptT (Variant e) m a -> ExceptT (Variant e') m a
OO.catch @NotAnInteger (forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
x -> m a
OO.throw forall b c a. (b -> c) -> (a -> b) -> a -> c
. NotAnInteger -> Errors1
Errors1NotAnInteger)
  forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> IO ()
IO.print Int
i
  forall (m :: * -> *) a. Monad m => a -> m a
return Int
i

--------------------------------------------------------------------------------
-- Embedding vanilla 'ExceptT' and 'Either' code into 'Oops' code.
-- We can call a function that throws multiple errors into a function
-- that only throws one by just catching and rethrowing the one
-- error.

-- We can call a function that throws vanilla 'ExceptT' errors and use 'onLeft'
-- to catch and rethrow the errors as oops errors.
example7 :: ()
  => MonadIO m
  => e `CouldBe` NotPositive
  => e `CouldBe` NotAnInteger
  => String
  -> ExceptT (Variant e) m Int
example7 :: forall (m :: * -> *) (e :: [*]).
(MonadIO m, CouldBe e NotPositive, CouldBe e NotAnInteger) =>
String -> ExceptT (Variant e) m Int
example7 String
s = do
  Int
i <- forall (m :: * -> *).
MonadIO m =>
String -> m (Either NotAnInteger Int)
readInt String
s
    forall a b. a -> (a -> b) -> b
& forall x (m :: * -> *) a.
Monad m =>
(x -> m a) -> m (Either x a) -> m a
OO.onLeft forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
x -> m a
OO.throw
  Int
pos <- forall (m :: * -> *).
MonadIO m =>
Int -> m (Either NotPositive Int)
requirePositive Int
i
    forall a b. a -> (a -> b) -> b
& forall x (m :: * -> *) a.
Monad m =>
(x -> m a) -> m (Either x a) -> m a
OO.onLeft forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
x -> m a
OO.throw
  forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> IO ()
IO.print Int
pos
  forall (m :: * -> *) a. Monad m => a -> m a
return Int
pos
  where
    readInt :: MonadIO m => String -> m (Either NotAnInteger Int)
    readInt :: forall (m :: * -> *).
MonadIO m =>
String -> m (Either NotAnInteger Int)
readInt = forall a. HasCallStack => String -> a
error String
"unimplemented"
    requirePositive :: MonadIO m => Int -> m (Either NotPositive Int)
    requirePositive :: forall (m :: * -> *).
MonadIO m =>
Int -> m (Either NotPositive Int)
requirePositive = forall a. HasCallStack => String -> a
error String
"unimplemented"

-- 'onLeftThrow' is shorthand for 'onLeft throw'.
example8 :: ()
  => MonadIO m
  => e `CouldBe` NotPositive
  => e `CouldBe` NotAnInteger
  => String
  -> ExceptT (Variant e) m Int
example8 :: forall (m :: * -> *) (e :: [*]).
(MonadIO m, CouldBe e NotPositive, CouldBe e NotAnInteger) =>
String -> ExceptT (Variant e) m Int
example8 String
s = do
  Int
i <- forall (m :: * -> *).
MonadIO m =>
String -> m (Either NotAnInteger Int)
readInt String
s
    forall a b. a -> (a -> b) -> b
& forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
m (Either x a) -> m a
OO.onLeftThrow
  Int
pos <- forall (m :: * -> *).
MonadIO m =>
Int -> m (Either NotPositive Int)
requirePositive Int
i
    forall a b. a -> (a -> b) -> b
& forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
m (Either x a) -> m a
OO.onLeftThrow
  forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> IO ()
IO.print Int
pos
  forall (m :: * -> *) a. Monad m => a -> m a
return Int
pos
  where
    readInt :: MonadIO m => String -> m (Either NotAnInteger Int)
    readInt :: forall (m :: * -> *).
MonadIO m =>
String -> m (Either NotAnInteger Int)
readInt = forall a. HasCallStack => String -> a
error String
"unimplemented"
    requirePositive :: MonadIO m => Int -> m (Either NotPositive Int)
    requirePositive :: forall (m :: * -> *).
MonadIO m =>
Int -> m (Either NotPositive Int)
requirePositive = forall a. HasCallStack => String -> a
error String
"unimplemented"

-- We can similarly call a function that throws via vanilla 'ExceptT'
example9 :: ()
  => MonadIO m
  => e `CouldBe` NotPositive
  => e `CouldBe` NotAnInteger
  => String
  -> ExceptT (Variant e) m Int
example9 :: forall (m :: * -> *) (e :: [*]).
(MonadIO m, CouldBe e NotPositive, CouldBe e NotAnInteger) =>
String -> ExceptT (Variant e) m Int
example9 String
s = do
  Int
i <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT (String -> ExceptT NotAnInteger IO Int
readInt String
s))
    forall a b. a -> (a -> b) -> b
& forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
m (Either x a) -> m a
OO.onLeftThrow
  Int
pos <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT (Int -> ExceptT NotPositive IO Int
requirePositive Int
i))
    forall a b. a -> (a -> b) -> b
& forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
m (Either x a) -> m a
OO.onLeftThrow
  forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> IO ()
IO.print Int
pos
  forall (m :: * -> *) a. Monad m => a -> m a
return Int
pos
  where
    readInt :: String -> ExceptT NotAnInteger IO Int
    readInt :: String -> ExceptT NotAnInteger IO Int
readInt = forall a. HasCallStack => String -> a
error String
"unimplemented"
    requirePositive :: Int -> ExceptT NotPositive IO Int
    requirePositive :: Int -> ExceptT NotPositive IO Int
requirePositive = forall a. HasCallStack => String -> a
error String
"unimplemented"

-- We can similarly call a function that throws via vanilla 'ExceptT'
example10 :: ()
  => MonadIO m
  => e `CouldBe` NotPositive
  => e `CouldBe` NotAnInteger
  => String
  -> ExceptT (Variant e) m Int
example10 :: forall (m :: * -> *) (e :: [*]).
(MonadIO m, CouldBe e NotPositive, CouldBe e NotAnInteger) =>
String -> ExceptT (Variant e) m Int
example10 String
s = do
  Int
i <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT (String -> ExceptT NotAnInteger IO Int
readInt String
s))
    forall a b. a -> (a -> b) -> b
& forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
m (Either x a) -> m a
OO.onLeftThrow
  Int
pos <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (forall e (m :: * -> *) a. ExceptT e m a -> m (Either e a)
runExceptT (Int -> ExceptT NotPositive IO Int
requirePositive Int
i))
    forall a b. a -> (a -> b) -> b
& forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
m (Either x a) -> m a
OO.onLeftThrow
  forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> IO ()
IO.print Int
pos
  forall (m :: * -> *) a. Monad m => a -> m a
return Int
pos
  where
    readInt :: String -> ExceptT NotAnInteger IO Int
    readInt :: String -> ExceptT NotAnInteger IO Int
readInt = forall a. HasCallStack => String -> a
error String
"unimplemented"
    requirePositive :: Int -> ExceptT NotPositive IO Int
    requirePositive :: Int -> ExceptT NotPositive IO Int
requirePositive = forall a. HasCallStack => String -> a
error String
"unimplemented"

-- We can also catch runtime exceptions and rethrow them as checked exceptions.
example11 :: ()
  => MonadIO m
  => MonadCatch m
  => e `CouldBe` IOException
  => ExceptT (Variant e) m String
example11 :: forall (m :: * -> *) (e :: [*]).
(MonadIO m, MonadCatch m, CouldBe e IOException) =>
ExceptT (Variant e) m String
example11 = do
  String
i <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (String -> IO String
IO.readFile String
"moo")
    forall a b. a -> (a -> b) -> b
& forall x (m :: * -> *) a.
(MonadCatch m, Exception x) =>
(x -> m a) -> m a -> m a
OO.onException @IOException forall x (e :: [*]) (m :: * -> *) a.
(MonadError (Variant e) m, CouldBe e x) =>
x -> m a
OO.throw
  forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> IO ()
IO.print String
i
  forall (m :: * -> *) a. Monad m => a -> m a
return String
i

-- The 'onExceptionThrow' is shorthand for 'onException throw'.
example12 :: ()
  => MonadIO m
  => MonadCatch m
  => e `CouldBe` IOException
  => ExceptT (Variant e) m String
example12 :: forall (m :: * -> *) (e :: [*]).
(MonadIO m, MonadCatch m, CouldBe e IOException) =>
ExceptT (Variant e) m String
example12 = do
  String
i <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (String -> IO String
IO.readFile String
"moo")
    forall a b. a -> (a -> b) -> b
& forall x (e :: [*]) (m :: * -> *) a.
(MonadCatch m, Exception x, MonadError (Variant e) m,
 CouldBe e x) =>
m a -> m a
OO.onExceptionThrow @IOException
  forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> IO ()
IO.print String
i
  forall (m :: * -> *) a. Monad m => a -> m a
return String
i