Skip to content

Commit 9981920

Browse files
committed
Add Library example
1 parent acd289d commit 9981920

File tree

4 files changed

+168
-4
lines changed

4 files changed

+168
-4
lines changed

app/library/Main.hs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
{-# LANGUAGE TemplateHaskell #-}
2+
3+
module Main where
4+
5+
import Control.Monad.IO.Class ( MonadIO
6+
, liftIO
7+
)
8+
import Control.Monad.Logger as L
9+
import qualified Data.Text as T
10+
11+
import Library as Lib
12+
13+
main :: IO ()
14+
main = do
15+
putStrLn "Please enter some texts: "
16+
msg <- getLine
17+
foo msg
18+
foo' msg
19+
bar msg
20+
bar' msg
21+
foobar msg
22+
foobar' msg
23+
24+
-- call preFoo and supply a file based logging capability
25+
foo :: String -> IO ()
26+
foo msg = L.runFileLoggingT "/tmp/demo.log" $ do
27+
liftIO $ putStrLn ""
28+
$(L.logInfo) $ T.pack $ "[foo] Got user input: " ++ msg
29+
out <- preFoo msg
30+
liftIO $ putStrLn out
31+
32+
-- call preFooIO and the inner logging will be library default stderr based logging
33+
foo' :: String -> IO ()
34+
foo' msg = L.runFileLoggingT "/tmp/demo.log" $ do
35+
liftIO $ putStrLn ""
36+
$(L.logInfo) $ T.pack $ "[foo'] Got user input: " ++ msg
37+
out <- liftIO $ preFooIO msg
38+
liftIO $ putStrLn out
39+
40+
-- call runBarWith and supply file based logging action of caller
41+
bar :: String -> IO ()
42+
bar msg = L.runFileLoggingT "/tmp/demo.log" $ do
43+
liftIO $ putStrLn ""
44+
$(L.logInfo) $ T.pack $ "[bar] Got user input: " ++ msg
45+
out <- liftIO $ runBar $ preBar msg
46+
liftIO $ putStrLn out
47+
48+
-- call runBarWithLogger and the inner logging is fixed to stderr logging again
49+
bar' :: String -> IO ()
50+
bar' msg = L.runFileLoggingT "/tmp/demo.log" $ do
51+
liftIO $ putStrLn ""
52+
$(L.logInfo) $ T.pack $ "[bar'] Got user input: " ++ msg
53+
-- get the logging action from caller's logging context
54+
logAction <- L.askLoggerIO
55+
out <- liftIO $ runBarWithLogger (flip runLoggingT $ logAction) $ preBar msg
56+
liftIO $ putStrLn out
57+
58+
-- run runFooBarT and supply a file based logging capability
59+
foobar :: String -> IO ()
60+
foobar msg = L.runFileLoggingT "/tmp/demo.log" $ do
61+
liftIO $ putStrLn ""
62+
$(L.logInfo) $ T.pack $ "[foobar] Got user input: " ++ msg
63+
out <- runFooBarT $ preFooBar msg
64+
liftIO $ putStrLn out
65+
66+
-- run runFooBarIO and the inner logging will be library default stderr based logging
67+
foobar' :: String -> IO ()
68+
foobar' msg = L.runFileLoggingT "/tmp/demo.log" $ do
69+
liftIO $ putStrLn ""
70+
$(L.logInfo) $ T.pack $ "[foobar'] Got user input: " ++ msg
71+
out <- liftIO $ runFooBarIO $ preFooBar msg
72+
liftIO $ putStrLn out

default.nix

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{ mkDerivation, base, data-default-class, hpack, http-types
22
, iproute, monad-logger, mtl, network, safe-exceptions, stdenv
3-
, transformers, wai, warp
3+
, text, transformers, wai, warp
44
}:
55
mkDerivation {
66
pname = "haskell-demo";
@@ -10,16 +10,16 @@ mkDerivation {
1010
isExecutable = true;
1111
libraryHaskellDepends = [
1212
base data-default-class http-types iproute monad-logger mtl network
13-
safe-exceptions transformers wai warp
13+
safe-exceptions text transformers wai warp
1414
];
1515
libraryToolDepends = [ hpack ];
1616
executableHaskellDepends = [
1717
base data-default-class http-types iproute monad-logger mtl network
18-
safe-exceptions transformers wai warp
18+
safe-exceptions text transformers wai warp
1919
];
2020
testHaskellDepends = [
2121
base data-default-class http-types iproute monad-logger mtl network
22-
safe-exceptions transformers wai warp
22+
safe-exceptions text transformers wai warp
2323
];
2424
prePatch = "hpack";
2525
homepage = "https://github.com/MatrixAI/Haskell-Demo#readme";

package.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ dependencies:
2424
- warp
2525
- http-types
2626
- data-default-class
27+
- text
2728

2829
library:
2930
source-dirs: src
@@ -39,6 +40,7 @@ library:
3940
- Lib
4041
- FFI
4142
- Demo
43+
- Library
4244

4345
executables:
4446
haskell-demo-ffi-exe:
@@ -59,6 +61,15 @@ executables:
5961
- -with-rtsopts=-N
6062
dependencies:
6163
- haskell-demo
64+
haskell-demo-library-exe:
65+
main: Main.hs
66+
source-dirs: app/library
67+
ghc-options:
68+
- -threaded
69+
- -rtsopts
70+
- -with-rtsopts=-N
71+
dependencies:
72+
- haskell-demo
6273

6374
tests:
6475
haskell-demo-test:

src/Library.hs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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

Comments
 (0)