{-# LANGUAGE
    TypeApplications
  , ScopedTypeVariables
  , LambdaCase
#-}

-- | do blocks for CheckedExceptT that compose exceptions
module Control.Monad.CheckedExcept.QualifiedDo
  ( (>>=)
  , (>>)
  , pure
  , return
  , fail
  ) where

import Control.Monad.CheckedExcept
import Prelude hiding (Monad(..), Applicative(..), MonadFail(..))
import qualified Prelude

-- | Bind operator for t'CheckedExceptT' that allows chaining computations
-- that may expand the exception set.
(>>=) :: forall exceptions1 exceptions2 exceptions3 m a.
  ( Contains exceptions1 exceptions3
  , Contains exceptions2 exceptions3
  , Prelude.Monad m
  )
  => CheckedExceptT exceptions1 m a
  -> (a -> CheckedExceptT exceptions2 m a)
  -> CheckedExceptT exceptions3 m a
CheckedExceptT exceptions1 m a
m >>= :: forall (exceptions1 :: [*]) (exceptions2 :: [*])
       (exceptions3 :: [*]) (m :: * -> *) a.
(Contains exceptions1 exceptions3,
 Contains exceptions2 exceptions3, Monad m) =>
CheckedExceptT exceptions1 m a
-> (a -> CheckedExceptT exceptions2 m a)
-> CheckedExceptT exceptions3 m a
>>= a -> CheckedExceptT exceptions2 m a
f = do
  m (Either (OneOf exceptions3) a) -> CheckedExceptT exceptions3 m a
forall (exceptions :: [*]) (m :: * -> *) a.
m (Either (OneOf exceptions) a) -> CheckedExceptT exceptions m a
CheckedExceptT (m (Either (OneOf exceptions3) a)
 -> CheckedExceptT exceptions3 m a)
-> m (Either (OneOf exceptions3) a)
-> CheckedExceptT exceptions3 m a
forall a b. (a -> b) -> a -> b
$ do
    CheckedExceptT exceptions1 m a -> m (Either (OneOf exceptions1) a)
forall (exceptions :: [*]) (m :: * -> *) a.
CheckedExceptT exceptions m a -> m (Either (OneOf exceptions) a)
runCheckedExceptT CheckedExceptT exceptions1 m a
m m (Either (OneOf exceptions1) a)
-> (Either (OneOf exceptions1) a
    -> m (Either (OneOf exceptions3) a))
-> m (Either (OneOf exceptions3) a)
forall a b. m a -> (a -> m b) -> m b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
Prelude.>>= \case
      Left OneOf exceptions1
e -> Either (OneOf exceptions3) a -> m (Either (OneOf exceptions3) a)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
Prelude.pure (Either (OneOf exceptions3) a -> m (Either (OneOf exceptions3) a))
-> Either (OneOf exceptions3) a -> m (Either (OneOf exceptions3) a)
forall a b. (a -> b) -> a -> b
$ OneOf exceptions3 -> Either (OneOf exceptions3) a
forall a b. a -> Either a b
Left (forall (exceptions1 :: [*]) (exceptions2 :: [*]).
Contains exceptions1 exceptions2 =>
OneOf exceptions1 -> OneOf exceptions2
weakenOneOf @exceptions1 @exceptions3 OneOf exceptions1
e)
      Right a
a -> CheckedExceptT exceptions3 m a -> m (Either (OneOf exceptions3) a)
forall (exceptions :: [*]) (m :: * -> *) a.
CheckedExceptT exceptions m a -> m (Either (OneOf exceptions) a)
runCheckedExceptT (CheckedExceptT exceptions2 m a -> CheckedExceptT exceptions3 m a
forall (exceptions1 :: [*]) (exceptions2 :: [*]) (m :: * -> *) a.
(Functor m, Contains exceptions1 exceptions2) =>
CheckedExceptT exceptions1 m a -> CheckedExceptT exceptions2 m a
weakenExceptions (a -> CheckedExceptT exceptions2 m a
f a
a))

-- | 'pure' function for t'CheckedExceptT'.
pure :: Prelude.Monad m => a -> CheckedExceptT es m a
pure :: forall (m :: * -> *) a (es :: [*]).
Monad m =>
a -> CheckedExceptT es m a
pure = a -> CheckedExceptT es m a
forall a. a -> CheckedExceptT es m a
forall (f :: * -> *) a. Applicative f => a -> f a
Prelude.pure

-- | 'return' function for t'CheckedExceptT'.
return :: Prelude.Monad m => a -> CheckedExceptT es m a
return :: forall (m :: * -> *) a (es :: [*]).
Monad m =>
a -> CheckedExceptT es m a
return = a -> CheckedExceptT es m a
forall a. a -> CheckedExceptT es m a
forall (m :: * -> *) a. Monad m => a -> m a
Prelude.return

-- | Sequentially compose two actions, discarding any value produced by the first.
(>>) :: forall exceptions1 exceptions2 exceptions3 m a x.
  ( Contains exceptions1 exceptions3
  , Contains exceptions2 exceptions3
  , Prelude.Monad m
  )
  => CheckedExceptT exceptions1 m x
  -> CheckedExceptT exceptions2 m a
  -> CheckedExceptT exceptions3 m a
CheckedExceptT exceptions1 m x
a >> :: forall (exceptions1 :: [*]) (exceptions2 :: [*])
       (exceptions3 :: [*]) (m :: * -> *) a x.
(Contains exceptions1 exceptions3,
 Contains exceptions2 exceptions3, Monad m) =>
CheckedExceptT exceptions1 m x
-> CheckedExceptT exceptions2 m a -> CheckedExceptT exceptions3 m a
>> CheckedExceptT exceptions2 m a
b = CheckedExceptT exceptions1 m x -> CheckedExceptT exceptions3 m x
forall (exceptions1 :: [*]) (exceptions2 :: [*]) (m :: * -> *) a.
(Functor m, Contains exceptions1 exceptions2) =>
CheckedExceptT exceptions1 m a -> CheckedExceptT exceptions2 m a
weakenExceptions CheckedExceptT exceptions1 m x
a CheckedExceptT exceptions3 m x
-> CheckedExceptT exceptions3 m a -> CheckedExceptT exceptions3 m a
forall a b.
CheckedExceptT exceptions3 m a
-> CheckedExceptT exceptions3 m b -> CheckedExceptT exceptions3 m b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
Prelude.>> CheckedExceptT exceptions2 m a -> CheckedExceptT exceptions3 m a
forall (exceptions1 :: [*]) (exceptions2 :: [*]) (m :: * -> *) a.
(Functor m, Contains exceptions1 exceptions2) =>
CheckedExceptT exceptions1 m a -> CheckedExceptT exceptions2 m a
weakenExceptions CheckedExceptT exceptions2 m a
b

-- | 'fail' function for t'CheckedExceptT'.
fail :: Prelude.MonadFail m => String -> CheckedExceptT es m a
fail :: forall (m :: * -> *) (es :: [*]) a.
MonadFail m =>
String -> CheckedExceptT es m a
fail = String -> CheckedExceptT es m a
forall a. String -> CheckedExceptT es m a
forall (m :: * -> *) a. MonadFail m => String -> m a
Prelude.fail

{-
Example usage:

module CompTest where

import Control.Monad.CheckedExcept
import Control.Monad.Trans.Class (lift)
import qualified Control.Monad.CheckedExcept.QualifiedDo as CheckedExcept

testCE1 :: CheckedExceptT '[()] IO ()
testCE1 = CheckedExcept.do
  lift $ putStrLn "1"
  lift $ pure ()
  pure ()

testCE2 :: CheckedExceptT '[Int] IO ()
testCE2 = CheckedExcept.do
  lift $ putStrLn "2"
  throwCheckedException (1 :: Int)
  pure ()

testCE3 :: CheckedExceptT '[Bool] IO ()
testCE3 = CheckedExcept.do
  lift $ putStrLn "3"
  throwCheckedException False
  pure ()

testCE4 :: CheckedExceptT '[String] IO ()
testCE4 = CheckedExcept.do
  lift $ putStrLn "4"
  throwCheckedException "err"
  pure ()

testCE5 :: CheckedExceptT '[Char] IO ()
testCE5 = CheckedExcept.do
  lift $ putStrLn "5"
  throwCheckedException 'c'
  pure ()

testCE :: CheckedExceptT '[(), Int, Bool, String] IO ()
testCE = CheckedExcept.do
  () <- testCE1
  () <- testCE2
  () <- testCE3
  () <- testCE4
  -- () <- testCE5 -- doesn't compile
  pure ()

test :: CheckedExcept TestExceptions () -> IO ()
test ce = case runCheckedExcept ce of
  Left e -> do
    applyAll (putStrLn . encodeException) e
    -- or
    withOneOf @() e $ \() -> putStrLn "()"
    withOneOf @Int e $ \n -> print $ n + 1
    withOneOf @Bool e $ \_ -> pure ()
    -- or
    caseException e
      (  (\() -> putStrLn "()")
      <: (\n -> print $ n + 1)
      <: CaseAny (\x -> putStrLn $ encodeException x)
      -- <: (\b -> putStrLn "bool")
      -- <: (\s -> putStrLn "string")
      -- <: CaseEnd
      )
  Right () -> putStrLn "Right"

type TestExceptions = '[(), Int, Bool, String]

deriving via (ShowException ()) instance CheckedException ()
deriving via (ShowException Int) instance CheckedException Int
deriving via (ShowException Bool) instance CheckedException Bool
deriving via (ShowException String) instance CheckedException [Char]
deriving via (ShowException Char) instance CheckedException Char
-}