I think I first “got” monad transformers after reading the wonderful article Grok Haskell Monad Transformers by Dan Piponi. But it wasn’t without some frustration, and I think that was due to the use of arbitrary functions (test1, test2, go1, go2…). I found it hard to wrap my mind around the bigger issue of the transformer mechanics when I didn’t have a firm grasp on what the purpose of these individual functions were.
So, I wrote the ubiquitous “Guess a number” program, that many probably first wrote in BASIC, using Haskell and a State Transformer instead.
guess_a_number.hs, or improved version with error handling (submitted by tphyahoo@gmail.com).
module Main where
import System.Random
import Control.Monad.State
main = do answer <- getStdRandom (randomR (1,100)) -- think of a number
putStrLn "I'm thinking of a number between 1 and 100, can you guess it?"
guesses <- execStateT (guessingSession answer) 0
putStrLn $ "Success in " ++ (show guesses) ++ " tries."
guessSession :: Int -> StateT Int IO ()
guessSession answer =
do gs <- lift getLine -- get guess from user
let g = read gs -- convert to number
modify (+1) -- increment number of guesses
case compare g answer of
LT -> do lift $ putStrLn "Too low"
guessSession answer
GT -> do lift $ putStrLn "Too high"
guessSession answer
EQ -> lift $ putStrLn "Got it!"
It illustrates a few things rather nicely I think, especially given it is only 20 lines long.
Much thanks to Don and Cale from #haskell for showing me how to write a proper case statement!