## Question:

I studied the topic of Monad and decided to make a program to test my knowledge.

```
import Control.Monad.IO.Class (liftIO)
import qualified Control.Monad.State as ST
import Control.Monad.Trans.State (StateT (..), evalStateT, get, put)
import qualified Control.Monad.Trans.Writer as WT
import Data.List (sort)
import Prelude hiding (max)
import System.Random
randomSt :: (RandomGen g, Random a, Num a) => a -> ST.State g a
randomSt max = ST.state $ randomR (1, max)
lottery :: Integer -> Integer-> StateT [Integer] IO [Integer]
lottery 0 _ = get >>= return
lottery n max = do
xs <- get
x <- liftIO $ randomRIO (1, max)
if x `elem` xs
then lottery n max
else do put (x:xs)
lottery (n - 1) max
lotteryWt :: Integer -> Integer -> WT.WriterT [String] (StateT [Integer] (ST.State StdGen)) [Integer]
lotteryWt 0 _ = ST.lift get >>= return
lotteryWt n max = do
xs <- ST.lift get
x <- ST.lift . ST.lift $ randomSt max
g <- ST.lift . ST.lift $ get
WT.tell [show x ++ " " ++ show n ++ ", state from StateT " ++ show xs ++ ", state from State " ++ show g]
if x `elem` xs
then lotteryWt n max
else do ST.lift $ put (x:xs)
lotteryWt (n - 1) max
main :: IO ()
main = do x <- evalStateT (lottery 6 60) []
g <- newStdGen
let y = ST.evalState (evalStateT (WT.runWriterT (lotteryWt 6 60)) []) g
putStrLn $ show $ sort x
putStrLn $ show $ sort (fst y)
mapM_ putStrLn (snd y)
```

I have two stacks of Monad's one `StateT [Integer] IO [Integer]`

and the other `WriterT ...`

. For each lottery function, I extract the values from each Monad.

I wanted to understand if this is the right way to use several Monad. Is this type of use a good practice?

## Answer:

Yes, but note that there are two variants of Monad Transformers. The first is the one you're using, from the `transformers`

package, where you gain from being able to use many monads, but there's the problem of depending on the stack (and stack order).

The other way is using the `mtl`

style. It's similar to what you're doing, but transformers are classes rather than concrete wrappers.

(Instead of `WriterT wmr`

you have `class Monad m => MonadWriter wm`

, which composes better).

I've stretched myself a little more than I'd like in an example:

There's an excellent read on exactly this subject here: https://making.pusher.com/3-approaches-to-monadic-api-design-in-haskell/

It walks right over the concrete Monads (which you are using), the abstract ones using classes and then another approach – although they have others.

```
-- stack runghc --package mtl --package transformers
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
module Main where
import Control.Monad.Identity
import System.Random
-- transformers
import Control.Monad.Trans.State (StateT, evalStateT)
import Control.Monad.Trans.Writer (WriterT, runWriterT)
-- mtl
import Control.Monad.Writer.Class
import Control.Monad.State.Class
-- transformers ==============================================================
-- No seu programa a pilha é composta por
type AppInteiros m = StateT [Integer] m
type AppLogger m = WriterT [String] m
type AppRandom = StateT StdGen Identity
-- Usando MTL, tem classes de tipo para elas:
-- class Monad m => MonadState s m | m -> s
-- class (Monoid w, Monad m) => MonadWriter w m | m -> w
-- Se o tipo do seu programa agora é:
type AppTransformers r = WriterT [String] (
StateT [Integer] (
StateT StdGen Identity
)
) r
-- mtl =======================================================================
-- Agora ele poderia ser uma classe (!) :)
type MonadAbstratoApp m = ( MonadState ([Integer], StdGen) m
, MonadWriter [String] m
)
type AppMtl m r = WriterT [String] (StateT ([Integer], StdGen) m) r
main = do
g <- newStdGen
(result, logs) <- (evalStateT (runWriterT (run 6 60)) ([], g))
print result -- x
print logs -- [ "Generating random number...", ... ]
where
-- Isso aqui é hiper genérico e desacoplado. Você poderia ter funções que
-- explicitam de que tipo de efeitos dependem e as usar sem mais boilerplate
lotteryWt :: MonadAbstratoApp m => Integer -> Integer -> m [Integer]
lotteryWt 0 _ = fst <$> get
lotteryWt n max = do
xs <- fst <$> get
x <- randomSt max
logRandom x
lottery 10 100
if x `elem` xs
then run n max
else do
modify (\(_, g) -> (x:xs, g))
run (n - 1) max
-- Por examplo:
randomSt :: MonadState ([Integer], StdGen) m => Integer -> m Integer
randomSt max = do
(ts, g) <- get
let (x, g') = randomR (1, max) g
put (ts, g')
return x
logRandom :: MonadWriter [String] m => Integer -> m ()
logRandom x = tell [ "Generating random number...", show x ]
lottery :: MonadState ([Integer], StdGen) m => Integer -> Integer -> m [Integer]
lottery 0 _ = fst <$> get
lottery n max = do
(xs, _) <- get
x <- randomSt max
if x `elem` xs
then lottery n max
else do
modify (\(_, g) -> (x:xs, g))
lottery (n - 1) max
```