Introduction To Haskell

Lecture 8


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

-- 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



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.


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 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.


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 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"


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


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.



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"
  else return ()

return () has the type IO ()

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


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"
  else return ()

module Main where

import Control.Monad

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


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]

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

print is equivalent to


mapM takes care of the sequencing stuff

Prelude> mapM print [1,2,3]

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

Prelude> mapM_ print [1,2,3]


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


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