-
Notifications
You must be signed in to change notification settings - Fork 26
/
Monad.hs
50 lines (39 loc) · 1.54 KB
/
Monad.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
module Lib.App.Monad
( -- * Application monad
App (..)
, AppEnv
, runAppAsIO
) where
import Control.Exception (catch, throwIO, try)
import Control.Monad.Except (MonadError (..))
import Relude.Extra.Bifunctor (firstF)
import Lib.App.Env (Env)
import Lib.App.Error (AppError, AppException (..))
-- | 'Env' data type parameterized by 'App' monad
type AppEnv = Env App
-- | Main application monad.
newtype App a = App
{ unApp :: ReaderT AppEnv IO a
} deriving (Functor, Applicative, Monad, MonadIO, MonadReader AppEnv)
instance MonadError AppError App where
throwError :: AppError -> App a
throwError = liftIO . throwIO . AppException
{-# INLINE throwError #-}
catchError :: App a -> (AppError -> App a) -> App a
catchError action handler = App $ ReaderT $ \env -> do
let ioAction = runApp env action
ioAction `catch` \(AppException e) -> runApp env $ handler e
{-# INLINE catchError #-}
{- | Helper for running route handlers in IO. Catches exception of type
'AppException' and unwraps 'AppError' from it.
Do not use this function to run the application. Use runners with logging from
"Lib.Effects.Log" module to also log the error.
-}
runAppAsIO :: AppEnv -> App a -> IO (Either AppError a)
runAppAsIO env = firstF unAppException . try . runApp env
{- | Helper for running 'App'.
Do not use this function to run the application. Use runners with logging from
"Lib.Effects.Log" module to also log the error.
-}
runApp :: AppEnv -> App a -> IO a
runApp env = usingReaderT env . unApp