Skip to content

Commit c41812c

Browse files
committed
Add solutions to the monad transformers chapter
1 parent c9915e6 commit c41812c

File tree

11 files changed

+396
-0
lines changed

11 files changed

+396
-0
lines changed

package.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ dependencies:
3434
- checkers
3535
- hspec-checkers
3636
- split
37+
- scotty
38+
- wai
3739

3840
library:
3941
source-dirs: src
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module MonadTransformers.ChapterExercises.FixTheCode where
2+
3+
import Control.Monad.Trans.Maybe
4+
import Control.Monad.Trans.Class
5+
import Control.Monad
6+
7+
isValid :: String -> Bool
8+
isValid v = '!' `elem` v
9+
10+
maybeExcite :: MaybeT IO String
11+
maybeExcite = do
12+
-- v :: String because MaybeT IO String is a monad
13+
-- |_______| |____|
14+
-- m a
15+
-- thus v :: a, i.e. v :: String
16+
v <- (lift :: IO String -> MaybeT IO String) getLine
17+
guard $ isValid (v :: String)
18+
return v
19+
20+
doExcite :: IO ()
21+
doExcite = do
22+
putStrLn "say something excite!"
23+
-- excite :: Maybe String because (runMaybeT maybeExcite) :: IO (Maybe String)
24+
-- |__| |___________|
25+
-- m a
26+
excite <- runMaybeT (maybeExcite :: MaybeT IO String)
27+
case excite of
28+
Nothing -> putStrLn "MOAR EXCITE"
29+
Just e -> putStrLn ("Good, was very excite: " ++ e)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{-# LANGUAGE OverloadedStrings #-}
2+
module MonadTransformers.ChapterExercises.HitCounter where
3+
4+
import Control.Monad.Trans.Class
5+
import Control.Monad.Trans.Reader
6+
import Data.IORef
7+
import qualified Data.Map as M
8+
import Data.Maybe (fromMaybe)
9+
import Data.Text.Lazy (Text)
10+
import qualified Data.Text.Lazy as TL
11+
import System.Environment (getArgs)
12+
import Web.Scotty.Trans
13+
import Network.Wai.Internal
14+
15+
data Config =
16+
Config {
17+
-- that's one, one click!
18+
-- two...two clicks!
19+
-- Three BEAUTIFUL clicks! ah ah ahhhh
20+
counts :: IORef (M.Map Text Integer)
21+
, prefix :: Text
22+
}
23+
24+
-- Stuff inside ScottyT is, except for things that escape
25+
-- via IO, effectively read-only so we can't use StateT.
26+
-- It would overcomplicate things to attempt to do so and
27+
-- you should be using a proper database for production
28+
-- applications.
29+
type Scotty = ScottyT Text (ReaderT Config IO)
30+
type Handler = ActionT Text (ReaderT Config IO)
31+
32+
bumpBoomp :: Text
33+
-> M.Map Text Integer
34+
-> (M.Map Text Integer, Integer)
35+
bumpBoomp k m = (newM, n)
36+
where n = M.findWithDefault 1 k m
37+
newM = M.insert k (n + 1) m
38+
39+
updateAndGet :: Text -> Config -> IO Integer
40+
updateAndGet key config = do
41+
let counter = counts config :: IORef (M.Map Text Integer)
42+
(m, n) <- bumpBoomp key <$> (readIORef counter :: IO (M.Map Text Integer))
43+
-- Very important to use writeIORef or something equivalent
44+
-- otherwise the counter will not be updated
45+
writeIORef counter m
46+
return n
47+
48+
app :: Scotty ()
49+
app = get "/:key" $ do
50+
unprefixed <- param "key"
51+
p <- lift $ ReaderT (return . prefix)
52+
let key' = mappend p unprefixed
53+
newInteger <- lift $ ReaderT $ updateAndGet key'
54+
html $ mconcat [ "<h1>Success! Count was: "
55+
, TL.pack $ show newInteger
56+
, "</h1>"
57+
]
58+
59+
main :: IO ()
60+
main = do
61+
[prefixArg] <- getArgs
62+
counter <- newIORef M.empty
63+
let config = Config counter (TL.pack prefixArg)
64+
runR = flip runReaderT config :: ReaderT Config IO Response -> IO Response
65+
scottyT 3000 runR app
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
module MonadTransformers.ChapterExercises.Morra where
2+
3+
import Control.Monad
4+
import Control.Monad.Trans.State
5+
import System.Exit
6+
import System.Random
7+
8+
data GameState = GameState {
9+
score1 :: Int
10+
, score2 :: Int
11+
, trigram :: [Int]
12+
}
13+
deriving (Eq, Show)
14+
15+
gameOver :: GameState -> IO ()
16+
gameOver (GameState s1 s2 _)
17+
| s1 < 3 && s2 < 3 = return ()
18+
| otherwise = do
19+
putStrLn "- Game over"
20+
putStrLn $ "- Player got " ++ show s1 ++ " points"
21+
putStrLn $ "- Computer got " ++ show s2 ++ " points"
22+
if s1 == 3 then
23+
putStrLn "- Player wins the game"
24+
else
25+
putStrLn "- Computer wins the game"
26+
exitSuccess
27+
28+
makeMove :: GameState -> IO Int
29+
makeMove s = do
30+
let t = trigram s
31+
c1 = length $ filter (==1) t
32+
c2 = length $ filter (==2) t
33+
case compare c1 c2 of
34+
GT -> return 1
35+
LT -> return 2
36+
EQ -> randomRIO (1, 2)
37+
38+
updateTrigram :: Int -> [Int] -> [Int]
39+
updateTrigram x xs = take 3 (x : xs)
40+
41+
game :: StateT GameState IO Int
42+
game = forever $ StateT $ \s -> do
43+
p1 <- (read <$> getLine) :: IO Int
44+
putStrLn $ "P: " ++ show p1
45+
p2 <- makeMove s
46+
putStrLn $ "C: " ++ show p2
47+
let p = p1 + p2
48+
if odd p then
49+
putStrLn "- P wins"
50+
else
51+
putStrLn "- C wins"
52+
let (s1, s2) = if odd p then
53+
(score1 s +1, score2 s)
54+
else
55+
(score1 s, score2 s + 1)
56+
t = updateTrigram p1 $ trigram s
57+
newGameState = GameState s1 s2 t
58+
gameOver newGameState
59+
return (p, newGameState)
60+
61+
main :: IO ()
62+
main = do
63+
putStrLn "-- p is Player"
64+
putStrLn "-- c is Computer"
65+
putStrLn "-- Player is odds, computer is evens."
66+
(a, s) <- runStateT game (GameState 0 0 [])
67+
return ()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
module MonadTransformers.ChapterExercises.WriteTheCode where
2+
3+
import Control.Monad.Trans.Reader
4+
import Control.Monad.Trans.State
5+
import Data.Functor.Identity
6+
7+
-- Question 1
8+
rDec :: Num a => Reader a a
9+
rDec = ReaderT $ \r -> Identity (r - 1)
10+
11+
-- Question 2
12+
rDec' :: Num a => Reader a a
13+
rDec' = ReaderT $ fmap Identity ((-1) + )
14+
15+
-- Question 3
16+
rShow :: Show a => ReaderT a Identity String
17+
rShow = ReaderT $ \a -> Identity (show a)
18+
19+
-- Question 4
20+
rShow' :: Show a => ReaderT a Identity String
21+
rShow' = ReaderT (fmap Identity show)
22+
23+
-- Question 5
24+
rPrintAndInc :: (Num a, Show a) => ReaderT a IO a
25+
rPrintAndInc = ReaderT $ \r -> do
26+
putStrLn $ "Hi: " ++ show r
27+
return $ r + 1
28+
29+
-- Question 6
30+
sPrintIncAccum :: (Num a, Show a) => StateT a IO String
31+
sPrintIncAccum = StateT $ \s -> do
32+
putStrLn $ "Hi: " ++ show s
33+
return (show s, s + 1)
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
module MonadTransformers.EitherT.Exercises where
2+
3+
import Control.Applicative (liftA2)
4+
5+
newtype EitherT e m a = EitherT { runEitherT :: m (Either e a) }
6+
7+
-- Question 1
8+
instance (Functor m) => Functor (EitherT e m) where
9+
fmap f (EitherT mea) = EitherT $ (fmap . fmap) f mea
10+
11+
-- Question 2
12+
instance (Applicative m) => Applicative (EitherT e m) where
13+
pure = EitherT . pure . pure
14+
EitherT fmeab <*> EitherT mea = EitherT $ liftA2 (<*>) fmeab mea
15+
16+
-- Question 3
17+
instance (Monad m) => Monad (EitherT e m) where
18+
return = pure
19+
EitherT mea >>= f = EitherT $ do
20+
ea <- mea
21+
case ea of
22+
Left e -> return (Left e)
23+
Right a -> (runEitherT . f) a
24+
25+
-- Question 4
26+
swapEither :: Either e a -> Either a e
27+
swapEither (Left e) = Right e
28+
swapEither (Right a) = Left a
29+
30+
swapEitherT :: (Functor m) => EitherT e m a -> EitherT a m e
31+
swapEitherT (EitherT mea) = EitherT (fmap swapEither mea)
32+
33+
-- Question 5
34+
eitherT :: Monad m => (a -> m c) -> (b -> m c) -> EitherT a m b -> m c
35+
eitherT f g (EitherT mab) = do
36+
ab <- mab
37+
case ab of
38+
Left a -> f a
39+
Right b -> g b
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module MonadTransformers.LexicallyInnerIsStructurallyOuter.Exercise where
2+
3+
import Control.Monad.Trans.Except
4+
import Control.Monad.Trans.Maybe
5+
import Control.Monad.Trans.Reader
6+
7+
embedded :: MaybeT (ExceptT String (ReaderT () IO)) Int
8+
embedded = MaybeT $ ExceptT $ ReaderT $ return <$> const (Right (Just 1))
9+
10+
-- Or more explicitely
11+
embedded' :: MaybeT (ExceptT String (ReaderT () IO)) Int
12+
embedded' = MaybeT ma
13+
where ma :: (ExceptT String (ReaderT () IO)) (Maybe Int)
14+
ma = ExceptT mb
15+
where mb :: (ReaderT () IO) (Either String (Maybe Int))
16+
mb = ReaderT mc
17+
where mc :: () -> IO (Either String (Maybe Int))
18+
mc = return <$> const (Right (Just 1))
+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
module MonadTransformers.MonadIO.Exercises where
2+
3+
import Control.Monad.IO.Class
4+
import Control.Monad.Trans.Class
5+
6+
-- MaybeT
7+
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
8+
9+
instance (Functor m) => Functor (MaybeT m) where
10+
fmap = undefined
11+
12+
instance (Applicative m) => Applicative (MaybeT m) where
13+
pure = undefined
14+
(<*>) = undefined
15+
16+
instance (Monad m) => Monad (MaybeT m) where
17+
return = pure
18+
(>>=) = undefined
19+
20+
instance MonadTrans MaybeT where
21+
lift = MaybeT . fmap Just
22+
23+
instance (MonadIO m) => MonadIO (MaybeT m) where
24+
liftIO = lift . liftIO
25+
26+
-- ReaderT
27+
newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }
28+
29+
instance (Functor m) => Functor (ReaderT r m) where
30+
fmap = undefined
31+
32+
instance (Applicative m) => Applicative (ReaderT r m) where
33+
pure = undefined
34+
(<*>) = undefined
35+
36+
instance (Monad m) => Monad (ReaderT r m) where
37+
return = pure
38+
(>>=) = undefined
39+
40+
instance MonadTrans (ReaderT r) where
41+
lift = ReaderT . const
42+
43+
instance (MonadIO m) => MonadIO (ReaderT r m) where
44+
liftIO = lift . liftIO
45+
46+
-- StateT
47+
newtype StateT s m a = StateT { runStateT :: s -> m (a, s) }
48+
49+
instance (Functor m) => Functor (StateT s m) where
50+
fmap = undefined
51+
52+
instance (Monad m) => Applicative (StateT s m) where
53+
pure = undefined
54+
(<*>) = undefined
55+
56+
instance (Monad m) => Monad (StateT s m) where
57+
return = pure
58+
(>>=) = undefined
59+
60+
instance MonadTrans (StateT s) where
61+
lift ma = StateT $ \s -> do
62+
a <- ma
63+
return (a, s)
64+
65+
instance (MonadIO m ) => MonadIO (StateT s m) where
66+
liftIO = lift . liftIO
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module MonadTransformers.MonadTrans.Exercises where
2+
3+
import Control.Monad.Trans.Class
4+
5+
newtype EitherT e m a = EitherT { runEitherT :: m (Either e a) }
6+
7+
instance MonadTrans (EitherT e) where
8+
lift = EitherT . (fmap Right)
9+
10+
newtype StateT s m a = StateT { runStateT :: s -> m (a, s) }
11+
12+
instance MonadTrans (StateT s) where
13+
lift ma = StateT $ \s -> do
14+
a <- ma
15+
return (a, s)
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
module MonadTransformers.StateT.Exercises where
2+
3+
import Control.Applicative
4+
5+
newtype StateT s m a = StateT { runStateT :: s -> m (a, s)}
6+
7+
instance (Functor m) => Functor (StateT s m) where
8+
fmap f (StateT sma) = StateT $ \s -> fmap f' (sma s)
9+
where f' (a, s) = (f a, s)
10+
11+
-- See this Stackoverflow post the reason why we need a Monad instance of m
12+
-- https://stackoverflow.com/questions/18673525/is-it-possible-to-implement-applicative-m-applicative-statet-s-m
13+
instance (Monad m) => Applicative (StateT s m) where
14+
pure a = StateT $ \s -> pure (a, s)
15+
StateT smab <*> StateT sma = StateT $ \s -> do
16+
(ab, s') <- smab s
17+
(a, s'') <- sma s'
18+
return (ab a, s'')
19+
20+
instance (Monad m) => Monad (StateT s m) where
21+
return = pure
22+
StateT sma >>= asmb = StateT $ \s -> do
23+
(a, s') <- sma s
24+
runStateT (asmb a) s'

0 commit comments

Comments
 (0)