Learn You a Haskell for Great Good! - Miran Lipovaca [73]
ghci> 3
3
ghci> print 3
3
ghci> map (++"!") ["hey","ho","woo"]
["hey!","ho!","woo!"]
ghci> print $ map (++"!") ["hey","ho","woo"]
["hey!","ho!","woo!"]
When we want to print out strings, we usually use putStrLn because we don’t want the quotes around them. However, for printing out values of other types to the terminal, print is used the most often.
when
The when function is found in Control.Monad (to access it, use import Control.Monad). It’s interesting because in a do block, it looks like a flow-control statement, but it’s actually a normal function.
when takes a Bool and an I/O action, and if that Bool value is True, it returns the same I/O action that we supplied to it. However, if it’s False, it returns the return () action, which doesn’t do anything.
Here’s a small program that asks for some input and prints it back to the terminal, but only if that input is SWORDFISH:
import Control.Monad
main = do
input <- getLine
when (input == "SWORDFISH") $ do
putStrLn input
Without when, we would need to write the program like this:
main = do
input <- getLine
if (input == "SWORDFISH")
then putStrLn input
else return ()
As you can see, the when function is useful when we want to perform some I/O actions when a condition is met, but do nothing otherwise.
sequence
The sequence function takes a list of I/O actions and returns an I/O action that will perform those actions one after the other. The result that this I/O action yields will be a list of the results of all the I/O actions that were performed. For instance, we could do this:
main = do
a <- getLine
b <- getLine
c <- getLine
print [a,b,c]
Or we could do this:
main = do
rs <- sequence [getLine, getLine, getLine]
print rs
The results of both these versions are exactly the same. sequence [getLine, getLine, getLine] makes an I/O action that will perform getLine three times. If we bind that action to a name, the result is a list of all the results. So in this case, the result would be a list of three things that the user entered at the prompt.
A common pattern with sequence is when we map functions like print or putStrLn over lists. Executing map print [1,2,3,4] won’t create an I/O action, but instead will create a list of I/O actions. Effectively, this is the same as writing this:
[print 1, print 2, print 3, print 4]
If we want to transform that list of I/O actions into an I/O action, we must sequence it:
ghci> sequence $ map print [1,2,3,4,5]
1
2
3
4
5
[(),(),(),(),()]
But what’s with the [(),(),(),(),()] at the end of the output? Well, when we evaluate an I/O action in GHCi, that action is performed, and then its result is printed out, unless that result is (). That’s why evaluating putStrLn "hehe" in GHCi just prints out hehe—putStrLn "hehe" yields (). But when we enter getLine in GHCi, the result of that I/O action is printed out, because getLine has a type of IO String.
mapM
Because mapping a function that returns an I/O action over a list and then sequencing it is so common, the utility functions mapM and mapM_ were introduced. mapM takes a function and a list, maps the function over the list, and then sequences it. mapM_ does the same thing, but it throws away the result later. We usually use mapM_ when we don’t care what result our sequenced I/O actions have. Here’s an example of mapM:
ghci> mapM print [1,2,3]
1
2
3
[(),(),()]
But we don’t care about the list of three units at the end, so it’s better to use this form:
ghci> mapM_ print [1,2,3]
1
2
3
forever
The forever function takes an I/O action and returns an I/O action that just repeats the I/O action it got forever. It’s located in Control.Monad. The following little program will indefinitely ask the user for some input and spit it back in all uppercase characters:
import Control.Monad
import Data.Char
main = forever $ do
putStr "Give me some input: "
l <- getLine
putStrLn $ map toUpper l
forM
forM (located in Control.Monad) is like mapM, but its parameters are switched around. The