| CS 331 Spring 2009 > Additional Lecture Notes for Wednesday, March 4, 2009 |
Here is “Hello World” in Haskell.
main = putStrLn "Hello, World!"
Function putStrLn takes a String and prints it, followed by a newline. Function putStr does the same thing, but without the newline.
main = putStr "No newline after this."
We can convert a value to a String (if this is possible) using show.
main = putStrLn s where
s1 = "My favorite number is "
s2 = show (7*7-7) -- s2 is a String
s = s1 ++ s2
“putStrLn "Hello, World!"” is an example of an I/O action. We can put multiple I/O actions together to make a single I/O action using “do”.
main = do
putStr "My "
putStr "favorite "
putStr "number is "
print (7*7-7) -- print x = putStrLn (show x)
Again, a do-block containing I/O actions is an I/O action. Thus, we can combine these with other I/O actions, using do. We can also call normal (non-I/O) functions inside I/O actions.
favNum = (7*7-7)
f1 = do
putStr "number "
putStr "is "
main = do
putStrLn "My "
do
putStrLn "favorite "
f1
f2
where
f2 = do
print favNum
To read a line, use getLine. This gives an I/O action that returns a value: a String holding the line read (without the newline). Use “<-” to bind a name to this return value, as shown below.
main = do
putStr "Type something: "
line <- getLine
putStr "You typed (backwards): "
putStrLn (reverse line)
An I/O action does not actually do anything. This would be a side effect, and there are no side effects in Haskell. Rather, an I/O action returns a record of the I/O that would be done, if side effects were legal. For example, get the type of getLine (in Hugs: :t getLine. The result:
getLine :: IO String
This means that getLine does not really return a String. Rather, it returns an I/O action that is a wrapper around a String. (This is why we do not say “line = getLine” when we want the string.)
So, if an I/O function cannot actually do anything, how does the I/O happen? It happens when all the I/O actions are wrapped up into a single I/O action and returned from main. The outside world is allowed to do things; that is where the I/O actually happens.
Some corollaries of this:
When you read input, you always get a String, but you often want to convert it to something else. For this, use read, which converts a String to whatever you want. Thus, read is the opposite of show.
main = do
line <- getLine
print num where
num = read line -- Really should check for errors here!
As noted in the code, read can produce an error. However, we are not covering how to handle this right now.
Three things to know.
With those in mind, a do block is a list of 3 kinds of things.
For example, we could rewrite the above code as follows.
main = do
line <- getLine
let num = read line -- Really should check for errors here!
print num
If we want to write a function to return the read value (IO-wrapped!), we could do it this way.
readVal = do
line <- getLine
let num = read line -- Really should check for errors here!
return num
When we do I/O, we generally want to repeat things: read all the lines of a file, etc. How do we repeat when there are no loops? Use recursion. For example, here is an implementation of getLine.
myGetLine = myGetLine_recurse "" where
myGetLine_recurse str = do
c <- getChar -- getChar gets a Char (duh)
if (c == '\n')
then return str
else myGetLine_recurse (str ++ [c])
And here is a main program to read a number and print the number plus ten, continuing until zero is read. This calls the above myGetLine.
main = do
putStr "Type a number (0 to end): "
line <- myGetLine
let value = read line -- "let" for non-I/O values
-- "read" converts a String to anything
if (value == 0) -- From here on is single expression
then return () -- "return ()": a do-nothing I/O action
else do
putStr "Adding 10, we get: "
putStrLn (show (value + 10))
main -- Recurse for "loop"
-- Tail recursion, can easily be eliminated
| CS 331 Spring 2009: Additional Lecture Notes for Wednesday, March 4, 2009 / Updated: 12 Mar 2009 / Glenn G. Chappell / ffggc@uaf.edu |
|