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 Monad's stacks one StateT [Integer] IO [Integer]
and the other WriterT ...
. For each lottery function, I extract the values of each Monad.
I wanted to understand if this is the right way to use multiple Monad. Is this practice a good practice?