I’m new to haskell, and i read through and digested Learn You A Haskell For Great Good, trying out a couple of things along the way. For my first project i wanted to try the classic: FizzBuzz. So i came up with the following code:
import System.IO
fizzBuzz :: (Integral a) => a -> String
fizzBuzz num
| fizz && buzz = "FizzBuzz"
| fizz = "Fizz"
| buzz = "Buzz"
| otherwise = show num
where fizz = num `mod` 3 == 0
buzz = num `mod` 5 == 0
main = print $ map fizzBuzz [1..100]
Worked great, except i got a rather dense looking list that was hard to read. So i tried this main function instead:
main = map putStrLn $ map fizzBuzz [1..100]
And that gives me the error Couldn't match expected type 'IO t' against inferred type '[IO ()]'. I tried half a dozen things and none of it seemed to help. What’s the proper way to do what i’m trying to do?
You’ve got a list of
IO ()actions.You need to join them into a single
IO ()action.What you want to do is to perform each of those
IO ()actions in sequence/sequence_:For convenience, mapM/mapM_ will map a function over a list and sequence the resulting monadic results.
So your fixed code would look like this:
Although I’d probably write it like this:
Or even this:
Let’s write our own
sequence. What do we want it to do?IOmonad, this means execute) the first result.sequencethe rest of the list; bind that list of results.GHC’s library uses something more like
foldr (liftM2 (:)) (return [])but that’s harder to explain to a newcomer; for now, just take my word that they’re equivalent.sequence_is easier, since it doesn’t bother keeping track of the results. GHC’s library implements it assequence_ ms = foldr (>>) (return ()) ms. Let’s just expand the definition offoldr:In other words, “do
a, discard the result; dob; discard the result, … finally, return()“.On the other hand, you don’t even need to know monads at all with the alternate
unlinessolution.What does
unlinesdo? Well,lines "a\nb\nc\nd\n" = ["a", "b", "c", "d"], so of courseunlines ["a", "b", "c", "d"] = "a\nb\nc\nd\n".unlines $ map fizzBuzz [1..100]=unlines ["1", "2", "Fizz", ..]="1\n2\nFizz\n..."and off it goes toputStr. Thanks to the magic of Haskell’s laziness, the full string never needs to be constructed in memory, so this will happily go to[1..1000000]or higher 🙂