# functional-programming – How to use Monad batteries?

## Question:

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

``````import           Control.Monad.IO.Class     (liftIO)
import           Control.Monad.Trans.State  (StateT (..), evalStateT, get, put)
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?

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:

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 System.Random

-- transformers

-- mtl

-- 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 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
``````
Scroll to Top