|
| 1 | +{-# LANGUAGE GeneralizedNewtypeDeriving #-} |
| 2 | +{-# LANGUAGE OverloadedStrings #-} |
| 3 | +{-# LANGUAGE TemplateHaskell #-} |
| 4 | + |
| 5 | +module Library where |
| 6 | + |
| 7 | +import Control.Monad.IO.Class ( MonadIO |
| 8 | + , liftIO |
| 9 | + ) |
| 10 | +import Control.Monad.Logger as L |
| 11 | +import Control.Monad.Logger ( LoggingT |
| 12 | + , MonadLogger |
| 13 | + ) |
| 14 | +import Control.Monad.Reader ( MonadReader |
| 15 | + , ReaderT |
| 16 | + , ask |
| 17 | + , runReaderT |
| 18 | + ) |
| 19 | +import qualified Data.Text as T |
| 20 | + |
| 21 | +-------------------------------- |
| 22 | +-- Library Style 1: mtl class -- |
| 23 | +-------------------------------- |
| 24 | +-- Main entry point via MonadLogger |
| 25 | +-- Expects caller of the library to supply logging capability via MonadLogger |
| 26 | +preFoo :: (MonadLogger m) => String -> m String |
| 27 | +preFoo s = $(L.logInfo) "Adding foo as prefix" >> return ("Foo " ++ s) |
| 28 | + |
| 29 | +-- Alternative entry point via IO with default logging capability |
| 30 | +preFooIO :: String -> IO String |
| 31 | +preFooIO = L.runStderrLoggingT . preFoo |
| 32 | + |
| 33 | +---------------------------------------------- |
| 34 | +-- Library Style 2: monad transformer stack -- |
| 35 | +---------------------------------------------- |
| 36 | +-- Library has its own monadic context with a fully specified stack transformer. |
| 37 | +-- Logging capability is present in the stack as LoggingT |
| 38 | +newtype Bar a = Bar {unBar :: ReaderT String (LoggingT IO) a} deriving (Functor, Applicative, Monad, MonadReader String, MonadLogger) |
| 39 | + |
| 40 | +-- Library function returns operations wrapped in its monadic context |
| 41 | +preBar :: String -> Bar String |
| 42 | +preBar s = do |
| 43 | + prefix <- ask |
| 44 | + $(L.logInfo) $ T.pack $ "Adding " ++ prefix ++ " as prefix" |
| 45 | + return $ prefix ++ " " ++ s |
| 46 | + |
| 47 | +-- Main entry point with configurable logging action |
| 48 | +runBarWithLogger :: (LoggingT IO a -> IO a) -> Bar a -> IO a |
| 49 | +runBarWithLogger runLog bar = runLog $ runReaderT (unBar bar) "Bar" |
| 50 | + |
| 51 | +-- Alternative entry point with default logging action |
| 52 | +runBar :: Bar a -> IO a |
| 53 | +runBar = runBarWithLogger runStderrLoggingT |
| 54 | + |
| 55 | +------------------------------------------------------------------ |
| 56 | +-- Library Style 3: mixed monad transformer stack and mtl class -- |
| 57 | +------------------------------------------------------------------ |
| 58 | +-- Library has its own monadic context (ReaderT String here), |
| 59 | +-- and also allows caller to supply further contexts (capabilities). |
| 60 | +-- Non fully specified stack transformer. LoggingT and IO not present in the stack |
| 61 | +newtype FooBarT m a = FooBarT {unFooBarT :: ReaderT String m a} deriving (Functor, Applicative, Monad, MonadReader String, MonadLogger) |
| 62 | + |
| 63 | +-- Alternative/convenient type with fully specified transformer stack |
| 64 | +type FooBarIO = FooBarT (LoggingT IO) |
| 65 | + |
| 66 | +-- Library function returns operation wrapped in both its own monadic context |
| 67 | +-- and that suppied by caller |
| 68 | +preFooBar :: (MonadLogger m) => String -> FooBarT m String |
| 69 | +preFooBar s = do |
| 70 | + prefix <- ask |
| 71 | + $(L.logInfo) $ T.pack $ "Adding " ++ prefix ++ " as prefix" |
| 72 | + return $ prefix ++ " " ++ s |
| 73 | + |
| 74 | +-- Main entry point mtl class |
| 75 | +-- Smart deconstructor expects caller to provide logging capability |
| 76 | +runFooBarT :: (MonadLogger m) => FooBarT m a -> m a |
| 77 | +runFooBarT fb = runReaderT (unFooBarT fb) "FooBar" |
| 78 | + |
| 79 | +-- Alternative entry point via IO with default logging capability |
| 80 | +runFooBarIO :: FooBarIO a -> IO a |
| 81 | +runFooBarIO = runStderrLoggingT . runFooBarT |
0 commit comments