Haskell markov text generator I'm new to Haskell, and here is my first not-totally-trivial program. It's the first program I tend to write in any language -- a Markov text generator. I'm wondering what I can change to make it more idiomatic, or what language features I could make better use of.

import Data.List
import System.Environment
import qualified Data.Map as Map
import Control.Monad.State
import System.Random

type MarkovMap = Map.Map String String
type MarkovState = (MarkovMap, StdGen, String)

transition :: State MarkovState Char
transition = do 
    (m, gen, sofar) &lt;- get
    let options = m Map.! sofar
        (index, newGen) = randomR (0, length options - 1) gen
        next = options !! index
    put (m, newGen, tail sofar ++ [next])
    return next

generateText :: State MarkovState Char -&gt; State MarkovState String
generateText s = do
    x &lt;- s
    xs &lt;- generateText s
    return (x:xs)

getWords :: MarkovMap -&gt; Int -&gt; [String]
getWords m n =
    let keys        = filter ((==) ' ' . last) $ Map.keys m 
        (r, gen)    = randomR (0, length keys - 1) $ mkStdGen 137
        startState  = keys !! r 
        markovChars = evalState (generateText transition) (m, gen, startState)
    in  take n . words $ markovChars

printWords :: [String] -&gt; IO ()
printWords ws = mapM_ putStrLn $ makeLines ws
    where makeLines [] = []
          makeLines ws = unwords (take 10 ws) : makeLines (drop 10 ws)

main :: IO ()
main = do
    (n:nwords:fileName:[]) &lt;- getArgs
    contents &lt;- readFile fileName
    let chainMap = chain (read n :: Int) . unwords . words $ contents
    printWords $ getWords chainMap (read nwords :: Int)

chain :: Int -&gt; String -&gt; Map.Map String String
chain n xs = 
    let from = map (take n) . tails $ xs ++ " " ++ xs
        to   = drop n . map (:[]) $ xs ++ " " ++ take n xs
    in Map.fromListWith (++) $ zip from to


Example usage:

Keep track of the last 3 characters, take 100 words from tobeornottobe.txt

&gt; runhaskell markov.hs 3 100 tobeornottobe.txt

delay, the law's count with who would bear things all;
and that make and sweary life; fortal shocks the hue
of returns tural consience of somethis againsolution. To die, thers
the he law's the have, or nobler retus resolence to
troublesh is noblesh is sicklied of regards of some will,
and arrows of? To beary from who would by a
life; for inst give spurns, and, but to sleep: perchan
flesh is heir thing afterprises us for no mortal shocks
turns of action devoutly to dreathe sleep: perchance thance the
ills we hue of time, to suffled of great pith
First of all, it seems like you never change the MarkovMap in your States. So, why don't you take it out of MarkovState and change the type of, say, transition, to MarkovMap -> State MarkovState Char?

Try to use more combinators and less pattern-matching. For example, the generate function is something like sequence . repeat.