Number Guessing Game in Haskell
Changing the where to a let allows you to skip passing the secret. Passing guesses in and messages out purifies the control flow part of the program. zip [1..] makes clear: The number of tries during each step does not depend on the control flow. interact, lines and foldr replace the recursion. (Recursion is too powerful for brains to handle in general, so we should avoid it when we have a simpler tool.) Your mileage may vary on any of these suggestions.

secret &lt;- randomRIO (smallest, biggest) :: IO Integer
let foo :: (Integer, Integer) -&gt; String -&gt; String
    foo (tries, guess) = case compare guess secret of
      LT -&gt; (++) "Too small!\n"
      GT -&gt; (++) "Too big!\n"
      EQ -&gt; const $ printf "You found the number in %d tries!\n" tries
printf "Find the number between %d and %d.\n" smallest biggest
interact $ foldr foo undefined . zip [1..] . map read . lines