Introduction To Haskell

Lecture 8


Input/Output

Using These Slides

Every slide has a secret note.

  • On Chrome: press F12, then click Console
  • On IE: press F12, then click Console
  • On Firefox: Ctrl+Shift+k


Shortcut Keys:

, PgDn, n, j next slide
, PgUp, p, k prev slide
Esc enables ctrl+f globally

Review of Homework 7

Find the intersect of two lines using an existing package
A
B

-- Designed by Anish S. Tondwalkar
import Graphics.Gloss.Data.Point
import Graphics.Gloss.Geometry.Line

-- A line is specified by two points (a point is a pair of Floats)
data Line = L Point Point

-- takes two lines instead of two points
intersectLines (L a b) (L c d) = intersectLineLine a b c d




	    

Pure

Remember, Haskell is pure.


  • Functions can't have side-effects
  • Functions take in inputs and compute outputs
  • Nothing else happens in-between

    (no modification of global variables)



However, input/output is not at all pure. There is a side effect! By displaying output pixels on your monitor, you are changing the global state of your computer.

Output

Let's print out some output!

Create a file Main.hs and write the following code


module Main where

main = do
  putStrLn "Hello world!"
  putStrLn "main is a function"
  putStrLn "of type IO ()"
	    

Run the code

Use GHCi


$ ghci
Prelude> :load Main.hs 
*Main> main
		    

Or just use runhaskell


$ runhaskell Main.hs
		    

Or compile the code!


$ ghc --make Main.hs
$ ./Main
		    

main

main is the "entry point of a Haskell program" *

It must be an IO type.


$ ghci Main.hs
*Main> :t main
main :: IO ()
	    

It's like the main from Java or C++. When you compile Haskell code, the main function runs.

do

glues together multiple IO actions

do is a keyword in Haskell, like let or where.


main = do
  putStrLn "this line is the first IO action"
  putStrLn "putStrLn has type String -> IO ()"
  putStrLn "we are glueing together multiple IO ()"
	    

Input

Input is just as straight forward.

The getLine function binds input to a variable.


module Main where

main = do
  putStrLn "Why did the banker leave his job?"
  answer <- getLine
  putStrLn (if answer == "he lost interest"
            then "Correct!"
            else "Wrong!")
	    

<-

We have seen the <- symbol before


[ x | x<-[1..10], even x ]
	    

The symbol is pronounced "drawn from"



main = do
  input <- getLine
  putStrLn ("you wrote: " ++ input)
	    

"input is drawn from getLine"

IO

If you see IO, think side effects.

putStrLn displays something to your monitor. That's a side effect!


putStrLn gives back nothing, ()


Prelude> :t putStrLn
putStrLn :: String -> IO ()
	    

getLine gives back a String to use


Prelude> :t getLine 
getLine :: IO String
	    

Pure

Side-effects are isolated into I/O actions.

Pure code is separated from impure operations.

I/O actions only exist within other I/O actions.


Brilliant!

return

return is a function that "makes an I/O action out of a pure value" *


Prelude> :t return
return :: Monad m => a -> m a
	    

return "hello" :: IO String


It's pretty much the opposite of the <- syntax.


main = do
  input <- return "hello"
  putStrLn input
	    

return packs up a value into an IO box. <- extracts the value out of an IO box.

Using return

In this program, we exit when the input is "y"


module Main where

main = do
  putStrLn "quit the program? y/n"
  ans <- getLine
  if ans /= "y" then do
    putStrLn "not quitting"
    main
  else return ()
	    

return () has the type IO ()

We create an IO that doesn't do anything so that the program could exit.

when

The when function from Control.Monad module looks nicer


when :: Monad m => Bool -> m () -> m ()


Old code
New code

module Main where

main = do
  putStrLn "quit? y/n"
  ans <- getLine
  if ans /= "y" then do
    putStrLn "not quitting"
    main
  else return ()
		  

module Main where

import Control.Monad

main = do
  putStrLn "quit? y/n"
  ans <- getLine
  when (ans /= "y") $ do
    putStrLn "not quitting"
    main
	    

sequence

sequence evaluates each IO action from a list of actions and returns a list of IO outputs


sequence :: Monad m => [m a] -> m [a]



Prelude> sequence [getLine, getLine]
hello
world
["hello","world"]
	    

Prelude> sequence (map print [1,2])
1
2
[(),()]
	    

print is equivalent to putStrLn.show

mapM

mapM takes care of the sequencing stuff


Prelude> mapM print [1,2,3]
1
2
3
[(),(),()]
	    

and mapM_ is the same but doesn't output anything afterwards


Prelude> mapM_ print [1,2,3]
1
2
3
	      

interact

interact makes I/O really easy.

Just call interact on a String -> String function and you're done!


This code counts the number of characters per line

module Main where

main = interact countChars

countChars :: String -> String
countChars str =
    let allLines = lines str
        lengths  = map (show.length) allLines
    in unlines lengths
	    

File Input

Let's count the number of lines in a file, (verbose) (elegant)
It's best to keep the pure code separate from the ugly IO stuff

module Main where

import System.IO

main = do
  theInput <- readFile "input.txt"
  putStrLn (countLines theInput)

countLines :: String -> String
countLines str = (show (length (lines str)))
	      

File Output

Writing to file is done with writeFile


module Main where

import System.IO

main = do
  putStrLn "writing to file..."
  writeFile "output.txt" ['A'..'Z']
	    
writeFile will overwrite the file. Use appendFile if you'd like to append instead

Homework

Wrestling with Haskell

  1. Fill out this form!
  2. Research 5 facts about Monads
  3. Write an IO program that reverses text

    given a file containing some text, create an output file with everything in reverse

    input.txt output.txt
    
    hello world
    			
    
    dlrow olleh