CS240h: Functional systems in Haskell

Why Haskell?

Why take CS240h?

Administrivia

Final project

Getting started with Haskell

Bindings

Haskell is a pure functional language

How to program without mutable variables?

Tail recursion

Guards and where clauses

Tip: variable names

Every expression and binding has a type

More on types

User-defined data types

Using data types

Exercise: Rock, Paper, Scissors referee

GHCi, version 7.10.3: http://www.haskell.org/ghc/  :? for help
...
*Main> outcome Rock Paper
Lose
*Main> outcome Scissors Paper
Win
*Main> outcome Paper Paper
Tie

Answer

data Move = Rock | Paper | Scissors deriving (Eq, Read, Show, Enum, Bounded)

data Outcome = Lose | Tie | Win deriving (Show, Eq, Ord)

-- | @outcome our_move their_move@
outcome :: Move -> Move -> Outcome
outcome Rock Scissors        = Win
outcome Paper Rock           = Win
outcome Scissors Paper       = Win
outcome us them | us == them = Tie
                | otherwise  = Lose

Parameterized types

More deconstruction tips

Lists

Some basic list functions in Prelude

head :: [a] -> a
head (x:_) = x
head []    = error "head: empty list"
tail :: [a] -> [a]           -- all but first element
tail (_:xs) = xs
tail []     = error "tail: empty list"
(++) :: [a] -> [a] -> [a]    -- infix operator concatenates lists
[] ++ ys     = ys
(x:xs) ++ ys = x : xs ++ ys
length :: [a] -> Int         -- This code is from language spec
length []    =  0            -- GHC implements differently, why?
length (_:l) =  1 + length l
filter :: (a -> Bool) -> [a] -> [a]
filter pred [] = []
filter pred (x:xs)
  | pred x     = x : filter pred xs
  | otherwise  = filter pred xs

Note function error :: String -> a reports assertion failures

Parsing with deriving Read and reads

Exercise: Using reads

*Main> parseMove "Rock"
Just Rock
*Main> parseMove "Paper"
Just Paper
*Main> parseMove "Scissors plus extra junk"
Nothing

Possible solutions

Being more permissive of line disciplines

Hoogle

Example: counting letters

Function composition

Lambda abstraction

Infix vs. Prefix notation

Fixity

Fixity of specific operators

infixl 9  !!             -- This is the default when fixity unspecified
infixr 9  .
infixr 8  ^, ^^, ⋆⋆
infixl 7  ⋆, /, `quot`, `rem`, `div`, `mod`  
infixl 6  +, -           -- Unary negation "-" has this fixity, too
infixr 5  ++             -- built-in ":" constructor has this fixity, too
infix  4  ==, /=, <, <=, >=, >, `elem`, `notElem`
infixr 3  &&
infixr 2  ||
infixl 1  >>, >>=
infixr 1  =<<  
infixr 0  $, $!, `seq`

The “infixr 0” operators

Accumulators revisited

factorial n0 = loop 1 n0
    where loop acc n | n > 1     = loop (acc * n) (n - 1)
                     | otherwise = acc
factorial n0 = loop 1 n0
    where loop acc n | n > 1     = (loop $! acc * n) (n - 1)
                     | otherwise = acc
factorial n0 = loop 1 n0
    where loop acc n | n > 1     = acc `seq` loop (acc * n) (n - 1)
                     | otherwise = acc

Haskell stack

Modules and import syntax

do notation

module Main where
import System.IO

greet h = do
  hPutStrLn h "What is your name?"
  name <- hGetLine h
  hPutStrLn h $ "Hi, " ++ name

withTty = withFile "/dev/tty" ReadWriteMode

main = withTty greet

do notation

greet h = do
  hPutStrLn h "What is your name?"
  name <- hGetLine h
  hPutStrLn h $ "Hi, " ++ name

What are the types of IO actions?

main :: IO ()
greet :: Handle -> IO ()
hPutStrLn :: Handle -> String -> IO ()
hGetLine :: Handle -> IO String

Another way to see IO [Peyton Jones]

do name <- hGetLine h
   hPutStrLn h $ "Hi, " ++ name

Another way to see IO [Peyton Jones]

do name <- hGetLine h
   hPutStrLn h $ "Hi, " ++ name

Running greet

$ ghc --make -dynamic greet
[1 of 1] Compiling Main             ( greet.hs, greet.o )
Linking greet ...
$ ./greet
What is your name?
David
Hi, David

The return function

Point-free IO composition

Exercise: Rock, Paper, Scissors against the computer

A possible solution

getMove :: Handle -> IO Move
getMove h = do
  hPutStrLn h $ "Please enter one of " ++ show ([minBound..] :: [Move])
  -- Here is the added code:
  input <- hGetLine h
  case parseMove input of Just move -> return move
                          Nothing -> getMove h

computerVsUser :: Move -> Handle -> IO ()
computerVsUser computerMove h = do
  userMove <- getMove h
  let o = outcome userMove computerMove
  hPutStrLn h $ "You " ++ show o

More on polymorphism

id :: a -> a
id x = x
const :: a -> b -> a
const a _ = a
fst :: (a, b) -> a
fst (a, _) = a
snd :: (a, b) -> b
snd (_, b) = b
print a = putStrLn (show a)   -- what's the type?  a -> IO ()?
show a = ???                  -- how to implement?

Parametric vs. ad hoc polymorphism

Classes and Instances

The Context of a type declaration

The Dreaded Monomorphism Restriction (DMR)

The DMR continued

The DMR take-away message

Superclasses and instance contexts

Classes of parameterized types

More Functors

Kinds

The Monad class

class Monad m where
    (>>=) :: m a -> (a -> m b) -> m b
    return :: a -> m a
    fail :: String -> m a   -- called when pattern binding fails
    fail s = error s        -- default is to throw exception

    (>>) :: m a -> m b -> m b
    m >> k = m >>= \_ -> k

The Maybe monad

Field labels

Field labels – initialization and matching

data Point = Point { xCoord :: Double, yCoord :: Double }

Field labels – access and update

A few Miscellaneous points

Networking

Networking exercise

Solution

withClient :: PortID -> (Handle -> IO a) -> IO a
withClient listenPort fn = do
  s <- listenOn listenPort
  (h, host, port) <- accept s
  putStrLn $ "Connection from host " ++ host ++ " port " ++ show port
  sClose s  -- Only accept one client
  a <- fn h
  hClose h
  return a