I'm writing my first small programs in Haskell and still getting a feel for the syntax and idioms. I've written this Mad Libs implementation using recursive IO. I've used IO actions throughout and I'm sure there must be a better way of splitting up this code to separate pure functions from IO actions. Also, I'm not happy with the printf statement, but I couldn't find a native way to apply an arbitrary number of list items to printf.import Text.PrintfgetAnswer :: String -> IO StringgetAnswer question = do    putStrLn question    answer <- getLine    return answergetAnswers :: [String] -> [String] -> IO [String]getAnswers [] ys = return ysgetAnswers (x:xs) ys = do    answer <- getAnswer x    let answers = ys ++ [answer]    getAnswers xs answersmain = do    let questions = [Enter a noun:, Enter a verb:, Enter an adjective:, Enter an adverb:]    let madlib = Your %s is %s up a %s mountain %s.    answers <- getAnswers questions []    printf madlib (answers!!0) (answers!!1) (answers!!2) (answers!!3)    putStrLn 
Mad Libs using recursive IO for user input
beginner;haskell;formatting
Can we make getAnswer simpler or out of IO? Well, not really. You want to ask the use a question, and you want to get an answer. So all we could do is to reduce the amount of unnecessary code:getAnswer :: String -> IO StringgetAnswer question = putStrLn question >> getLine-- or--                 = do--      putStrLn question--      getLineHowever, getAnswers can be refactored quite heavily. First of all, its interface isn't really developer-friendly. What are the questions? What are the answers? We should probably hide that in the bowels of our function:getAnswers :: [String] -> IO [String]getAnswers xs = go xs []   where go [] ys = return ys        go (x:xs) ys = do          answer <- getAnswer x          let answers = ys ++ [answer]          go xs answersBut ++ [...] isn't really best-practice. Instead, you would ask all other questions and then combine them:  where go [] = return []        go (x:xs) = do          answer <- getAnswer x          otherAnswers <- getAnswers x          return (answer : otherAnswers)But at that point, we're merily copying mapM's functionailty. Therefore, your getAnswers should begetAnswers :: [String] -> IO [String]getAnswers = mapM getAnswerA lot simpler.Now for your main. If you don't know how many words you'll get you will need a list, correct. But lets check the structure of your result: Your %s is %s up a %s mountain %s.       1     2       3           4There is a pattern. We have our text, then whatever the user gave us, then again our text, and so on. Let's split that into fragments:[Your ,%s, is ,%s, up a ,%s, mountain ,%s,.]--       ^^^^        ^^^^          ^^^^              ^^^^This brings up the following idea: if you have a list of your answers, you only need the list of the other words, right?[Your , is , up a , mountain ,.]And then we need to zip that list with yours:interleave :: [a] -> [a] -> [a]interleave (x:xs) (y:ys) = x : y : interleave xs ysinterleave xs     _      = xsWe end up with the following main:main = do    let questions = [Enter a noun:, Enter a verb:, Enter an adjective:, Enter an adverb:]    let madlib = [Your , is , up a , mountain ,.]    answers <- getAnswers questions    putStrLn $ interleave madlib questionsHere's all the code at once:getAnswer :: String -> IO StringgetAnswer q = putStrLn q >> getLinegetAnswers :: [String] -> IO [String]getAnswers = mapM getAnswerinterleave :: [a] -> [a] -> [a]interleave (x:xs) (y:ys) = x : y : interleave xs ysinterleave xs     _      = xsmain :: IO ()main = do    let questions = [Enter a noun:, Enter a verb:, Enter an adjective:, Enter an adverb:]    let madlib = [Your , is , up a , mountain ,.]    answers <- getAnswers questions    putStrLn $ interleave madlib questionsExercisesThe interleave function above is left-biased. Why? Could this pose problems for your program? Why not?