Online Book Reader

Home Category

Learn You a Haskell for Great Good! - Miran Lipovaca [71]

By Root 423 0
to names, let is used when we just want to give names to normal values inside I/O actions. It’s similar to the let syntax in list comprehensions.

Let’s take a look at an I/O action that uses both <- and let to bind names.

import Data.Char

main = do

putStrLn "What's your first name?"

firstName <- getLine

putStrLn "What's your last name?"

lastName <- getLine

let bigFirstName = map toUpper firstName

bigLastName = map toUpper lastName

putStrLn $ "hey " ++ bigFirstName ++ " "

++ bigLastName

++ ", how are you?"

See how the I/O actions in the do block are lined up? Also notice how the let is lined up with the I/O actions, and the names of the let are lined up with each other? That’s good practice, because indentation is important in Haskell.

We wrote map toUpper firstName, which turns something like "John" into a much cooler string like "JOHN". We bound that uppercased string to a name and then used it in a string that we printed to the terminal.

You may be wondering when to use <- and when to use let bindings. <- is for performing I/O actions and binding their results to names. map toUpper firstName, however, isn’t an I/O action—it’s a pure expression in Haskell. So you can use <- when you want to bind the results of I/O actions to names, and you can use let bindings to bind pure expressions to names. Had we done something like let firstName = getLine, we would have just called the getLine I/O action a different name, and we would still need to run it through a <- to perform it and bind its result.

Putting It in Reverse


To get a better feel for doing I/O in Haskell, let’s make a simple program that continuously reads a line and prints out the same line with the words reversed. The program’s execution will stop when we input a blank line. This is the program:

main = do

line <- getLine

if null line

then return ()

else do

putStrLn $ reverseWords line

main

reverseWords :: String -> String

reverseWords = unwords . map reverse . words

To get a feel for what it does, save it as reverse.hs, and then compile and run it:

$ ghc --make reverse.hs

[1 of 1] Compiling Main ( reverse.hs, reverse.o )

Linking reverse ...

$ ./reverse

clean up on aisle number nine

naelc pu no elsia rebmun enin

the goat of error shines a light upon your life

eht taog fo rorre senihs a thgil nopu ruoy efil

it was all a dream

ti saw lla a maerd

Our reverseWords function is just a normal function. It takes a string like "hey there man" and applies words to it to produce a list of words like ["hey","there","man"]. We map reverse over the list, getting ["yeh","ereht","nam"], and then we put that back into one string by using unwords. The final result is "yeh ereht nam".

What about main? First, we get a line from the terminal by performing getLine and call that line line. Next we have a conditional expression. Remember that in Haskell, every if must have a corresponding else, because every expression must have some sort of value. Our if says that when a condition is true (in our case, the line that we entered is blank), we perform one I/O action; when it isn’t true, the I/O action under the else is performed.

Because we need to have exactly one I/O action after the else, we use a do block to glue together two I/O actions into one. We could also write that part as follows:

else (do

putStrLn $ reverseWords line

main)

This makes it clearer that the do block can be viewed as one I/O action, but it’s uglier.

Inside the do block, we apply reverseWords to the line that we got from getLine and then print that to the terminal. After that, we just perform main. It’s performed recursively, and that’s okay, because main is itself an I/O action. So in a sense, we go back to the start of the program.

If null line is True, the code after the then is executed: return (). You might have used a return keyword in other languages to return from a subroutine or function. But return in Haskell is nothing like the return in most other languages.

In Haskell (and in I/O actions specifically), return makes an I/O action out of a pure value.

Return Main Page Previous Page Next Page

®Online Book Reader