Introduction to Functional Programming

Joel E. Kaasinen

Spring 2018

Lecture 1: …and so it Begins

Contents

Administrative information

Course structure

Grading

Read these

Haskell

Some History

Running Haskell

Let’s start!

$ stack ghci
GHCi, version 8.0.1: http://www.haskell.org/ghc/  :? for help
Prelude> 1+1
2
Prelude> "asdf"
"asdf"
Prelude> reverse "asdf"
"fdsa"
Prelude> :t "asdf"
"asdf" :: [Char]
Prelude> tail "asdf"
"sdf"
Prelude> :t tail "asdf"
tail "asdf" :: [Char]
Prelude> :t tail
tail :: [a] -> [a]

Expressions and types

Expression Type Value
1+1 Int 2
not True Bool False

Syntax of expressions

Haskell C
f 1 f(1)
f 1 2 f(1,2)
g f 1 g(f,1)
g (f 1) g(f(1))
a + b a + b
f a + g b f(a) + g(b)
f (a + g b) f(a+g(b))
f a (g b) f(a,g(b))

Syntax of types

Type Literals Use Operations
Int 1, 2, -3 The usual number type +, -, *, div, mod
Integer 1, 2, 900000000000000000 Unbounded number type +, -, *, div, mod
Double 0.1, 1.2e5 Floating point numbers +, -, *, /, sqrt
Bool True, False Truth values &&, ||, not
String "abcd", "" Strings of characters reverse, ++

The structure of a Haskell program

module Gold where

-- The golden ratio
phi :: Double
phi = (sqrt 5 + 1) / 2

polynomial :: Double -> Double
polynomial x = x^2 - x - 1

f x = polynomial (polynomial x)

main = do
  print (polynomial phi)
  print (f phi)

The structure of a Haskell program

module Gold where
phi :: Double
polynomial :: Double -> Double
phi = (sqrt 5 + 1) / 2
f x = polynomial (polynomial x)
-- The golden ratio

How do I get anything done?

Conditionals

Java:

int y = (x == 0) ? 3 : x;

Haskell:

y = if x == 0 then 3 else 4

Example

factorial n = if n==0
              then 1
              else n * factorial (n-1)

Local definitions

circleArea :: Double -> Double
circleArea r = pi * rsquare
    where pi = 3.1415926
          rsquare = r * r
circleArea r = let pi = 3.1415926
                   rsquare = r * r
               in pi * rsquare
circleArea r = pi * square r
    where pi = 3.1415926
          square x = x * x
circleArea r = let pi = 3.1415926
                   square x = x * x
               in pi * square r

Pattern matching

Examples

greet :: String -> String -> String
greet "Suomi"    name = "Hei. " ++ name
greet "Italia"   name = "Ciao bella! " ++ name
greet "Englanti" name = "How do you do? " ++ name
greet _          name = "Hello. " ++ name
describe :: Integer -> String
describe 0 = "zero"
describe 1 = "one"
describe 2 = "an even prime"
describe n = "the number " ++ show n
funny :: Bool -> Integer -> Integer
funny True 0 = 1
funny True n = n
funny False 0 = 0
funny False 1 = 1
funny False n = -1

Recursion

Examples

factorial 0 = 1
factorial n = n * factorial (n-1)
-- compute the sum 1^2+2^2+3^2+...+n^2
squareSum 0 = 0
squareSum n = n^2 + squareSum (n-1)
-- Fibonacci numbers, slow version
fibonacci 1 = 1
fibonacci 2 = 1
fibonacci n = fibonacci (n-2) + fibonacci (n-1)

More recursion

Examples

Java:

public int fibonacci(int n) {
    int a = 0;
    int b = 1;
    while (n>1) {
        int c = a+b;
        a=b;
        b=c;
        n--;
    }
    return b;
}

Haskell:

-- fibonacci numbers, fast version
fibonacci :: Integer -> Integer
fibonacci n = fibonacci' 0 1 n

fibonacci' :: Integer -> Integer -> Integer -> Integer
fibonacci' a b 1 = b
fibonacci' a b n = fibonacci' b (a+b) (n-1)

All together now!

module Collatz where

step :: Integer -> Integer
step x = if even x then down else up
  where down = div x 2
        up = 3*x+1

collatz :: Integer -> Integer
collatz 1 = 0
collatz x = 1 + collatz (step x)

longest :: Integer -> Integer
longest upperBound = longest' 0 upperBound

longest' :: Integer -> Integer -> Integer
longest' l 0 = l
longest' l n = if l' > l
               then longest' l' (n-1)
               else longest' l (n-1)
  where l' = collatz n

A word about indentation

Indentation examples:

i x = let y = x+x+x+x+x+x in div y 5

j x = let y = x+x+x
              +x+x+x
      in div y 5

k = a + b
  where a = 1
        b = 1

l = a + b
  where
    a = 1
    b = 1
i x = let y = x+x+x+x+x+x
in div y 5

j x = let y = x+x+x
      +x+x+x
      in div y 5

k = a + b
  where a = 1
      b = 1

l = a + b
where
  a = 1
  b = 1

l = a + b
  where
    a = 1
     b = 1

A word about purity

Working on the exercises

Lecture 2: Catamorphic

Contents

Guards

f x y z
  | condition1 x y z = something
  | condition2 x z   = other
  | otherwise        = somethingother

Guards: examples

describe :: Int -> String
describe n
  | n==2      = "Two"
  | even n    = "Even"
  | n==3      = "Three"
  | n>100     = "Big!!"
  | otherwise = "The number "++show n
factorial
  | n<0       = -1
  | n==0      = 1
  | otherwise = n * factorial (n-1)
-- guards and pattern matching!
complicated :: Bool -> Double -> Double -> Double
complicated True x y
  | x == y    = 0
  | otherwise = x / (x-y)
complicated False x y
  | y < 0     = sin x
  | otherwise = sin x + sin y

Lists

[0,3,4,1+1]
[True,True,False] :: [Bool]
["Moi","Hei"] :: [String]
[] :: [a] -- more about this later
[[1,2],[3,4]] :: [[Int]]

List operations

-- returns the first element
head :: [a] -> a
-- returns everything except the first element
tail :: [a] -> [a]
-- returns the n first elements
take :: Int -> [a] -> [a]
-- returns everything except the n first elements
drop :: Int -> [a] -> [a]
-- lists are catenated with the ++ operator
(++) :: [a] -> [a] -> [a]
-- lists are indexed with the !! operator
(!!) :: [a] -> Int -> a
-- is this list empty?
null :: [a] -> Bool
-- the length of a list
length :: [a] -> Int
Prelude> :t "asdf"
"asdf" :: [Char]

Examples

[7,10,4,5] !! 2
  ==> 4
f xs = take 2 xs ++ drop 4 xs
f [1,2,3,4,5,6]  ==>  [1,2,5,6]
f [1,2,3]        ==>  [1,2]
g xs = tail xs ++ [head xs]
g [1,2,3]      ==>  [2,3,1]
g (g [1,2,3])  ==>  [3,1,2]

A word about type inference and polymorphism

Examples of type inference

tail :: [a] -> [a]
tail [True,False] :: [Bool]
head :: [a] -> a
head [True,False] :: Bool
Prelude> [True,False] ++ "Moi"

<interactive>:1:16:
    Couldn't match expected type `Bool' against inferred type `Char'
      Expected type: [Bool]
      Inferred type: [Char]
    In the second argument of `(++)', namely `"Moi"'
    In the expression: [True, False] ++ "Moi"
f xs ys = [head xs, head ys]
g xs = f "Moi" xs
Prelude> :t f
f :: [a] -> [a] -> [a]
Prelude> :t g
g :: [Char] -> [Char]

A word about type annotations

Functional programming, at last

applyTo1 :: (Int -> Int) -> Int
applyTo1 f = f 1

addThree :: Int -> Int
addThree x = x + 3
applyTo1 addThree ==> addThree 1 ==> 1 + 3 ==> 4

More functional programming

-- converts a list by applying the given function to the elements
map :: (a -> b) -> [a] -> [b]
map addThree [1,2,3] ==> [4,5,6]
-- selects the elements from a list that fulfill a condition
filter :: (a -> Bool) -> [a] -> [a]

positive :: Int -> Bool
positive x = x>0
filter positive [0,1,-1,3,-3] ==> [1,3]

Even more functional programming

palindromes n = filter palindrome (map show [1..n])
  where palindrome str = str == reverse str
length (palindromes 9999) ==> 198
-- These are from the Data.List module in the standard library
tails :: [a] -> [[a]]        -- return a list of suffixes of a given list
sort :: Ord a => [a] -> [a]  -- sorts a list
contexts :: Int -> Char -> String -> [String]
contexts k c s = sort (map tail (filter match (map process (tails s))))
  where match [] = False
        match (c':_) = c==c'
        process x = take (k+1) x
contexts 2 'a' "abracadabra" ==> ["","br","br","ca","da"]

Partial application

f :: Bool -> Integer -> Integer -> Integer -> Integer
f True  x _ z = x+z
f False _ y z = y+z
Prelude> (f True) 1 2 3
4
Prelude> let g = (f True) in g 1 2 3
4
Prelude> let g = (f True 1) in g 2 3
4
Prelude> map (f True 1 2) [1,2,3]
[2,3,4]
Prelude> :t f True
f True :: Integer -> Integer -> Integer -> Integer
Prelude> :t f True 1
f True 1 :: Integer -> Integer -> Integer
Prelude> :t f True 1 2
f True 1 2 :: Integer -> Integer
Prelude> :t f True 1 2 3
f True 1 2 3 :: Integer

Partial application 2

Prelude> map (*2) [1,2,3]
[2,4,6]
Prelude> map (2*) [1,2,3]
[2,4,6]
Prelude> map (1/) [1,2,3,4,5]
[1.0,0.5,0.3333333333333333,0.25,0.2]

The . operator

(.) :: (b -> c) -> (a -> b) -> a -> c
(f.g) x ==> f (g x)
double x = 2*x
quadruple = double . double  -- computes 2*(2*x) == 4*x
f = quadruple . (+1)         -- computes 4*(x+1)
third = head . tail . tail   -- fetches the third element of a list

The $ operator

($) :: (a -> b) -> a -> b
f x (g y (h x y (i z z)))

as

f x . g y . h x y $ i z z
        f x . g y . h x y $ i z z
  ==>  (f x . g y . h x y) (i z z)
  ==>  (f x . g y) (h x y  (i z z))
  ==>  (f x  (g y  (h x y  (i z z))))

Lambdas

Prelude> (\x -> x*x) 3
9
Prelude> (\x -> reverse x == x) "ABBA"
True
Prelude> filter (\x -> reverse x == x) ["ABBA","ACDC","otto","lothar","anna"]
["ABBA","otto","anna"]
Prelude> (\x y -> x^2+y^2) 2 3
13
\x0 x1 x2 ... -> e

is the same as

let f x0 x1 x2 ... = e in f

Functional style

contexts :: Int -> Char -> String -> [String]
contexts k c s = sort (map tail (filter match (map process (tails s))))
  where match [] = False
        match (c':_) = c==c'
        process x = take (k+1) x
contexts k c s = sort (map tail (filter match (map (take (k+1)) (tails s))))
  where match [] = False
        match (c':_) = c==c'
contexts k c s = sort . map tail . filter match . map (take $ k+1) $ tails s
  where match [] = False
        match (c':_) = c==c'
contexts k c = sort . map tail . filter ((==[c]).take 1) . map (take $ k+1) . tails

More functional list wrangling examples

-- find the first substring that consists only of the given characters
findSubString :: [String] -> [String] -> [String]
findSubString chars = takeWhile (\x -> elem x chars)
                      . dropWhile (\x -> not $ elem x chars)
findSubString "abcd" "xxxyyyzabaaxxabcd"  ==>  "abaa"
-- split the string into pieces at the given character
split :: Char -> [String] -> [[String]]
split c [] = []
split c xs = start : split c (drop 1 rest)
  where start = takeWhile (/=c) xs
        rest = dropWhile (/=c) xs
split 'x' "fooxxbarxquux"   ==>   ["foo","","bar","quu"]

… and more!

-- from the module Data.Ord
-- compares two values "through" the function f
comparing :: (Ord a) => (b -> a) -> b -> b -> Ordering
comparing f x y = compare (f x) (f y)

-- from the module Data.List
-- sorts a list using the given comparison function
sortBy :: (a -> a -> Ordering) -> [a] -> [a]

-- sorts lists by their length
sortByLength :: [[a]] -> [[a]]
sortByLength = sortBy (comparing length)
sortByLength [[1,2,3],[4,5],[4,5,6,7]]   ==>  [[4,5],[1,2,3],[4,5,6,7]]

Colon

Prelude> 1:[]
[1]
Prelude> 1:[2,3]
[1,2,3]
Prelude> tail (1:[2,3])
[2,3]
Prelude> head (1:[2,3])
1
Prelude> :t (:)
(:) :: a -> [a] -> [a]
   (:)
  /   \
 1    (:)
     /   \
    2    (:)
        /   \
       3    []

Building a list

descend 0 = []
descend n = n : descend (n-1)
descend 4 ==> [4,3,2,1]
iterate f 0 x = [x]
iterate f n x = x : iterate f (n-1) (f x)
iterate (*2) 4 3 ==> [3,6,12,24,48]

let xs = "terve"
in iterate tail (length xs) xs
  ==> ["terve","erve","rve","ve","e",""]

Pattern matching for lists

myhead :: [Int] -> Int
myhead [] = -1
myhead (first:rest) = first

mytail :: [Int] -> [Int]
mytail [] = []
mytail (first:rest) = rest
sumFirstTwo :: [Integer] -> Integer
sumFirstTwo (a:b:_) = a+b
sumFirstTwo _       = 0

Consuming a list

sumNumbers :: [Int] -> Int
sumNumbers [] = 0
sumNumbers (x:xs) = x + sumNumbers xs
myMaximum :: [Int] -> Int
myMaximum [] = 0       -- actually this should be some sort of error...
myMaximum (x:xs) = go x xs
  where go biggest [] = biggest
        go biggest (x:xs) = go (max biggest x) xs
sum2d :: [[Int]] -> Int
sum2d []           = 0
sum2d ([]:xss)     = sum2d xss
sum2d ((x:xs):xss) = x + sum2d (xs:xss)

Building and consuming a list

doubleList :: [Int] -> [Int]
doubleList [] = []
doubleList (x:xs) = 2*x : doubleList xs
doubleList [1,2,3]
==> doubleList (1:(2:(3:[])))
==> 2*1 : doubleList (2:(3:[]))
==> 2*1 : (2*2 : doubleList (3:[]))
==> 2*1 : (2*2 : (2*3 : doubleList []))
==> 2*1 : (2*2 : (2*3 : []))
=== [2*1, 2*2, 2*3]
==> [2,4,6]

Building and consuming a list continued

map :: (a -> b) -> [a] -> [b]
map _ []     = []
map f (x:xs) = f x : map f xs
filter :: (a -> Bool) -> [a] -> [a]
filter _pred []    = []
filter pred (x:xs)
  | pred x         = x : filter pred xs
  | otherwise      = filter pred xs

Infinite lists

Prelude> repeat 1
[1,1,1,1,
^C
Prelude> take 10 $ repeat 1
[1,1,1,1,1,1,1,1,1,1]
Prelude> take 20 $ repeat 1
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
Prelude> repeat 1 !! 13337
1
Prelude> take 10 . map (2^) $ [0..]
[1,2,4,8,16,32,64,128,256,512]
Prelude> take 21 $ cycle "asdf"
"asdfasdfasdfasdfasdfa"
Prelude> take 4 . map (take 4) . tails $ cycle "asdf"
["asdf","sdfa","dfas","fasd"]
Prelude> head . filter (>10^8) $ map (3*) [0..]
100000002

Laziness & Purity

f x = f x   -- infinite recursion
g x y = x
f 1
g 2 (f 1)  ==>  2

Laziness Example

Prelude> head . filter (>100) $ map (3^) [0..]
243
    head (filter (>100) (map (3^) [0..]))
==> head (filter (>100) (map (3^) (0:[1..])))
==> head (filter (>100) (1 : map (3^) [1..]))
==> head (filter (>100) (map (3^) [1..]))
==> head (filter (>100) (map (3^) (1:[2..])))
==> head (filter (>100) (3 : map (3^) [2..]))
==> head (filter (>100) (map (3^) [2..]))
-- let's take bigger steps now
==> head (filter (>100) (9 : map (3^) [3..]))
==> head (filter (>100) (27 : map (3^) [4..]))
==> head (filter (>100) (81 : map (3^) [5..]))
==> head (filter (>100) (243 : map (3^) [6..]))
==> head (243 : filter (>100) (map (3^) [6..]))
==> 243

Lecture 3: You Need String for a Knot

Contents

Algebraic Datatypes: Introduction

data Bool = True | False
data Ordering = LT | EQ | GT
data Color = Red | Green | Blue

rgb :: Color -> [Double]
rgb Red = [1,0,0]
rgb Green = [0,1,0]
rgb Blue = [0,0,1]
Prelude> :t Red
Red :: Color
Prelude> :t [Red,Blue,Green]
[Red,Blue,Green] :: [Color]
Prelude> rgb Red
[1.0,0.0,0.0]

Algebraic Datatypes: Fields

data Report = MkReport Int String String
Prelude> :t MkReport 1 "a" "b"
MkReport 1 "a" "b" :: Report
reportContents :: Report -> String
reportContents (MkReport id title contents) = contents
setReportContents :: String -> Report -> Report
setReportContents contents (MkReport id title _contents) = MkReport id title contents

Algebraic Datatypes: Constructors

data Card = Joker | Heart Int | Club Int | Spade Int | Diamond Int
Prelude> :t Heart
Heart :: Int -> Card
Prelude> :t Club
Club :: Int -> Card
Prelude> map Heart [1,2,3]
[Heart 1,Heart 2,Heart 3]
Prelude> (Heart . succ) 3
Heart 4

deriving Show

Prelude> EQ
EQ
Prelude> True
True
Prelude> Joker
<interactive>:1:0:
    No instance for (Show Card)
      arising from a use of `print' at <interactive>:1:0-4
    Possible fix: add an instance declaration for (Show Card)
    In a stmt of a 'do' expression: print it
data Card = Joker | Heart Int | Club Int | Spade Int | Diamond Int
  deriving Show
Prelude> Joker
Joker

Example 1: Maybe I’m Amazed

Type Values
Maybe Bool Nothing, Just False, Just True
Maybe Int Nothing, Just 0, Just 1, …
Maybe [Int] Nothing, Just [], Just [1,1337], …

Example 1: continued

intOrZero :: Maybe Int -> Int
intOrZero Nothing = 0
intOrZero (Just i) = i

safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead (x:_) = Just x

headOrZero :: [Int] -> Int
headOrZero xs = case safeHead xs of Nothing -> 0
                                    Just x -> x
data Maybe a = Nothing | Just a

Algebraic Datatypes: type parameters

data Wrap a = MkWrap a
unwrap (MkWrap a) = a
Prelude> :t MkWrap
MkWrap :: a -> Wrap a
Prelude> :t unwrap
unwrap :: Wrap a -> a
Prelude> :t MkWrap True
MkWrap True :: Wrap Bool
Prelude> :t MkWrap []
MkWrap [] :: Wrap [a]
data Maybe a = Nothing | Just a

Syntactic note

data Wrap a = Wrap a
data Report = Report Int String String
Prelude> Maybe
<interactive>:1:1: error:
Data constructor not in scope: Maybe

Prelude> undefined :: Nothing
<interactive>:2:14: error:
    Not in scope: type constructor or classNothing

Example 2: Either you die a hero…

data Either a b = Left a | Right b
readInt :: String -> Either String Int
readInt "0" = Right 0
readInt "1" = Right 1
readInt s = Left ("Unsupported string "++show s)
iWantAString :: Either Int String -> String
iWantAString (Right str)   = str
iWantAString (Left number) = show number

Example 2: continued

iterateE :: (a -> Either b a) -> a -> b
iterateE f x = case f x of Left y  -> y
                           Right y -> iterateE f y

step :: Int -> Int -> Either Int Int
step k x = if x>k then Left x else Right (2*x)

goThru :: [a] -> Either a [a]
goThru [x] = Left x
goThru (x:xs) = Right xs
Prelude> iterateE (step 100) 1
128
Prelude> iterateE (step 1000) 3
1536
Prelude> iterateE goThru [1,2,3,4]
4

Algebraic Datatypes: recursion

data IntList = Empty | Node Int IntList
  deriving Show

ihead :: IntList -> Int
ihead (Node i _) = i

itail :: IntList -> IntList
itail (Node _ t) = t

ilength :: IntList -> Int
ilength Empty = 0
ilength (IntList _ t) = 1 + ilength t

Example 3: Let me list the ways

data List a = Empty | Node a (List a)
  deriving Show

lhead :: List a -> a
lhead (Node h _) = h

ltail :: List a -> List a
ltail (Node _ t) = t

lnull :: List a -> Bool
lnull Empty = True
lnull _     = False

llength :: List a -> Int
llength Empty = 0
llength (Node _ t) = 1 + llength t

Example 4: Growing a tree

data Tree a = Leaf | Node a (Tree a) (Tree a)
treeHeight :: Tree a -> Int
treeHeight Leaf = 0
treeHeight (Node _ l r) = 1 + max (treeHeight l) (treeHeight r)
treeHeight Leaf ==> 0
treeHeight (Node 2 Leaf Leaf)
  ==> 1 + max (treeHeight Leaf) (treeHeight leaf)
  ==> 1 + max 0 0
  ==> 1
treeHeight (Node 1 Leaf (Node 2 Leaf Leaf))
  ==> 1 + max (treeHeight Leaf) (treeHeight (Node 2 Leaf Leaf))
  ==> 1 + max 0 1
  ==> 2
treeHeight (Node 0 (Node 1 Leaf (Node 2 Leaf Leaf)) Leaf)
  ==> 1 + max (treeHeight (Node 1 Leaf (Node 2 Leaf Leaf))) (treeHeight Leaf)
  ==> 1 + max 2 0
  ==> 3

Example 4: continued

insert :: Int -> Tree Int -> Tree Int
insert x Leaf = Node x Leaf Leaf
insert x (Node y l r)
  | x < y = Node y (insert x l) r
  | x > y = Node y l (insert x r)
  | otherwise = Node y l r

lookup :: Int -> Tree Int -> Bool
lookup x Leaf = False
lookup x (Node y l r)
  | x < y = lookup x l
  | x > y = lookup x r
  | otherwise = True

Algebraic Datatypes: Summary

data TypeName = ConstructorName FieldType FieldType2 | AnotherConstructor FieldType3 | OneMoreCons
data TypeName variable = Cons1 variable Type1 | Cons2 Type2 variable
foo (ConstructorName a b) = a+b
foo (AnotherConstructor _) = 0
foo OneMoreCons = 7
ConstructorName :: FieldType -> FieldType2 -> TypeName
Cons1 :: a -> Type1 -> TypeName a

Immutability

     code                       memory
                          x             y
                          |             |
let x = [1,2,3,4]        (1:) - (2:) - (3:) - (4:) - []
    y = drop 2 x                       /
    z = 5:y                    z - (5:)

Path copying

[]     ++ ys = ys
(x:xs) ++ ys = x:(xs ++ ys)
      xs - (1:) - (2:) - (3:) - []

                         ys - (5:) - (6:) - []
                             /
xs ++ ys - (1:) - (2:) - (3:)

Path copying: contd.

insert :: Int -> Tree Int -> Tree Int
insert x Leaf = Node x Leaf Leaf
insert x (Node y l r)
  | x < y = Node y (insert x l) r
  | x > y = Node y l (insert x r)
  | otherwise = Node y l r
          t              insert 6 t
          |                  |
        Node 5            Node 5
       /______\__________/      \
      //       \                 \
 Node 3        Node 7            Node 7
 /    \        /    \           /      \
Leaf  Node 4  Leaf  Leaf    Node 6    Leaf
     /    \                /     \
   Leaf   Leaf           Leaf   Leaf

Something fun: Tying the Knot

  code             memory
let xs = 1:2:xs      xs - (1:) - (2:) -+
 in xs                     |           |
                           +-----------+
      code                                           memory
-- a Coin with a text and a flip side
data Coin = Side String Coin                  dollar
                                                |
dollar :: Coin                               Side "heads" --+
dollar = heads                                      ^       |
  where heads = Side "heads" tails                  |       v
        tails = Side "tails" heads                  +-- Side "tails"

Something fun: list comprehensions

[2*i | i<-[1,2,3]]
  ==> [2,4,6]
let f = (2*)
    lis = [0..10]
in [f x | x<-lis]
  ==> [0,2,4,6,8,10,12,14,16,18,20]
[(x,y) | x <- [1..7], even x, y <- [True,False]]
  ==> [(2,True),(2,False),(4,True),(4,False),(6,True),(6,False)]
[f x | x <- lis, p x]
map f (filter p lis)

Something fun: custom operators

(<+>) :: [Int] -> [Int] -> [Int]
xs <+> ys = zipWith (+) xs ys
(+++) :: String -> String -> String
a +++ b = a ++ " " ++ b
Prelude> 5 `div` 2
2
Prelude> (+1) `map` [1,2,3]
[2,3,4]

Lecture 4: RealWorld -> (a,RealWorld)

Contents

Oh right: case of

case expression of
  pattern -> expression
  pattern -> expression
myHead :: [Int] -> Int
myHead xs = case xs of (x:_) -> x
                       []    -> -1

You’ve been fooled

questionnaire = do
  putStrLn "Write something!"
  s <- getLine
  putStrLn $ "You wrote: "++s
import Network.HTTP
import Control.Monad

main = do
  rsp <- Network.HTTP.simpleHTTP $ getRequest "http://pseudo.fixme.fi/~opqdonut/ifp2018/words"
  s <- getResponseBody rsp
  forM_ (words s) $ \s -> do
     putStrLn "word:"
     putStrLn s

What’s going on?

Prelude> :t putStrLn
putStrLn :: String -> IO ()
Prelude> :t getLine
getLine :: IO String
Haskell type Java-type
foo :: IO () void foo()
bar :: IO a a bar()
f :: a -> b -> IO () void f(a arg0, b arg1)
g :: c -> IO d d g(c arg)

do as thou wilt

do operation
   operation arg
   variable <- operationThatReturnsStuff
   let var2 = expression
   operationThatProducesTheResult var2

Examples

query :: IO ()
query = do
  putStrLn "Write something!"
  s <- getLine
  let n = length s
  putStrLn $ "You wrote "++show n++" characters: "++s
returningQuery :: String -> IO String
returningQuery question = do
  putStrLn question
  getLine

return

produceThree :: IO Int
produceThree = return 3

printThree :: IO ()
printThree = do
  three <- produceThree
  putStrLn $ show three
yesNoQuestion :: String -> IO Bool
yesNoQuestion question = do
  putStrLn question
  s <- getLine
  return $ s == "Y"

return pitfalls

do return 1
   return 2
do ...
   x <- op
   return x
do ...
   op
return (f x : xs)
-- alternatively:
return $ f x : xs

do and types

Example 1

foo = do
  ...
  lastOp

Example 2

foo x y = do
  ...
  lastOp arg

Example 3

foo x = do
  ...
  return arg

More on <-

Control structures 1

printDescription :: Int -> IO ()
printDescription n
  | even n    = putStrLn "even"
  | n==3      = putStrLn "three"
  | otherwise = print n
readAndSum :: Int -> IO Int
readAndSum 0 = return 0
readAndSum n = do
  i <- readLn
  s <- readAndSum (n-1)
  return $ i+s
ask :: [String] -> IO [String]
ask [] = return []
ask (question:questions) = do
  putStr question
  putStrLn "?"
  answer <- getLine
  answers <- ask questions
  return $ answer:answers

Control structures 2

-- conditional operation
when :: Bool -> IO () -> IO ()
-- conditonal operation, vice versa
unless :: Bool -> IO () -> IO ()
-- do something many times
replicateM :: Int -> IO a -> IO [a]
-- do something many times, throw away the results
replicateM_ :: Int -> IO a -> IO ()
-- do something for every list element
mapM :: (a -> IO b) -> [a] -> IO [b]
-- do something for every list element, throw away the results
mapM_ :: (a -> IO b) -> [a] -> IO ()
-- the same, but arguments flipped
forM  :: [a] -> (a -> IO b) -> IO [b]
forM_ :: [a] -> (a -> IO b) -> IO ()
readAndSum n = do
  numbers <- replicateM n readLn
  return $ sum numbers
ask :: [String] -> IO [String]
ask questions = do
  forM questions $ \question -> do
    putStr question
    putStrLn "?"
    getLine

Useful operations

-- printing
putStr :: String -> IO ()
putStrLn :: String -> IO ()
print :: Show a => a -> IO ()
print = putStr . show
-- reading
getLine :: IO String
readLn :: Read a => IO a
-- simple operations:
-- FilePath is just String
readFile :: FilePath -> IO String
writeFile :: FilePath -> String -> IO ()
-- with file handles:
openFile :: FilePath -> IOMode -> IO Handle   -- IOMode is either ReadMode, WriteMode, or ReadWriteMode
hPutStr :: Handle -> String -> IO ()
hPutStrLn :: Handle -> String -> IO ()
hPrint :: (Show a) => Handle -> a -> IO ()
hGetLine :: Handle -> IO String
-- etc

String processing

-- split a string into lines
lines :: String -> [String]
-- build a string out of lines
unlines :: [String] -> String
-- split a string into words
words :: String -> [String]
-- build a string out of words
unwords :: [String] -> String
-- render a value into a string
show :: Show a => a -> String
-- read a string into a value (opposite of show!)
read :: Read a => String -> a

Let’s do something real

-- a line is a type signature if it contains :: but does not contain =
isTypeSignature :: String -> Bool
isTypeSignature s = not (isInfixOf "=" s) && isInfixOf "::" s

-- return list of types for a .hs file
readTypesFile :: FilePath -> IO [String]
readTypesFile file
  | isSuffixOf ".hs" file = do content <- readFile file
                               let ls = lines content
                               return $ filter isTypeSignature ls
  | otherwise             = return []

-- list children of directory, prepend directory name
qualifiedChildren path = do childs <- listDirectory path
                            return $ map (\name -> path++"/"++name) childs

readTypesDir :: FilePath -> IO [String]
readTypesDir path = do childs <- qualifiedChildren path
                       typess <- forM childs readTypes
                       return $ concat typess

-- read types contained in a file or directory
readTypes :: FilePath -> IO [String]
readTypes path = do isDir <- doesDirectoryExist path
                    if isDir then readTypesDir path else readTypesFile path

main = do ts <- readTypes "."
          mapM_ putStrLn ts

What does it all mean?

Prelude> let x = print 1   -- creates operation, doesn't run it
Prelude> x                 -- runs the operation
1
Prelude> x                 -- runs it again!
1

Operations are values

choice :: IO a -> IO a -> IO a
choice a b =
  do putStr "a or b? "
     x <- getLine
     case x of "a" -> a
               "b" -> b
               _ -> do putStrLn "Wrong!"
                       choice a b
cand :: IO Bool -> IO Bool -> IO Bool
cand a b = do bool <- a
              if bool
                then b
                else return False

Operations are values: more examples

opc :: IO a -> (a -> IO b) -> (a -> IO ()) -> IO b
opc open process close = do
   resource <- open
   result <- process resource
   close resource
   return result
firstLineOfFile path = opc (openFile path ReadMode) hGetFile hClose
withFile path op = opc (openFile path ReadMode) op hClose
connectDatabase :: IO Connection
execStmt :: Connection -> Statement -> IO Result
closeConnection :: Connection -> IO ()

execSqls :: [Statement] -> IO [Result]
execSqls stmts = opc connectDatabase (\conn -> mapM (execStmt conn) stmts) closeConnection

One more thing: IORef

newIORef :: a -> IO (IORef a)
readIORef :: IORef a -> IO a
writeIORef :: IORef a -> a -> IO ()
modifyIORef :: IORef a -> (a -> a) -> IO ()
sumList :: [Int] -> IO Int
sumList xs = do r <- newIORef 0
                mapM_ (\x -> modifyIORef r (x+)) xs
                readIORef r

Summary of IO

op :: X -> IO Y
op arg = do operation                 -- run operation
            operation2 arg            -- run operation with argument
            result <- operation3 arg  -- run operation with argument, store result
            let something = f result  -- run a pure function f, store result
            finalOperation            -- last operation produces the the return value

Oh right: tuples

fst :: (a, b) -> a
snd :: (a, b) -> b

Tuples: examples

findWithIndex :: (a -> Bool) -> [a] -> (a,Int)
findWithIndex p xs = go 0 xs
  where go i (x:xs)
          | p x       = (x,i)
          | otherwise = go (i+1) xs
Prelude Data.List> :t partition
partition :: (a -> Bool) -> [a] -> ([a], [a])
Prelude Data.List> partition (>0) [-1,1,-4,3,2,0]
([1,3,2],[-1,-4,0])
Prelude Data.List> case partition (>0) [-1,1,-4,3,2,0] of (a,b) -> a++b
[1,3,2,-1,-4,0]

Lecture 5: fmap fmap fmap

Contents

Type classes

(+) :: (Num a) => a -> a -> a
(==) :: (Eq a) => a -> a -> Bool

Type constraints

f :: (Int -> Int) -> Int -> Bool
f g x = x == g x
f :: (a -> a) -> a -> Bool
f g x = x == g x
    • No instance for (Eq a) arising from a use of ‘==’
      Possible fix:
        add (Eq a) to the context of
          the type signature for:
            f :: (a -> a) -> a -> Bool
    • In the expression: x == g x
      In an equation for ‘f’: f g x = x == g x
f :: (Eq a) => (a -> a) -> a -> Bool
f g x = x == g x

Type constraints continued

Prelude> let f g x = x == g x
Prelude> :type f
f :: (Eq a) => (a -> a) -> a -> Bool
g :: (Eq a, Eq b) => a -> a -> b -> b -> Bool
g a0 a1 b0 b1 = a0 == a1 || b0 == b1

Example: Eq for a custom type

data Color = Black | White

instance Eq Color where
  Black == Black  = True
  White == White  = True
  _     == _      = False

Working with type classes

class Size a where
  size :: a -> Int
instance Size Int where
  size x = x

instance Size [a] where
  size xs = length xs

Working with type classes, continued

class Foo a where
  empty :: a
  size :: a -> Int
  sameSize :: a -> a -> Bool

instance Foo (Maybe a) where
  empty = Nothing

  size Nothing = 0
  size (Just a) = 1

  sameSize x y = size x == size y

instance Foo [a] where
  empty = []
  size xs = length xs
  sameSize x y = size x == size y

Allowed instances

instance Foo [a] -- this is ok
instance Foo [Int] -- this is NOT ok
instance Foo (Maybe a) -- this is ok
instance Foo (Maybe [a]) -- this is NOT ok

Default implementations

class Example a where
  example :: a
  examples :: [a]
  examples = [example]

instance Example Int where
  example = 1
  examples = [0,1,2]

instance Example Bool where
  example = True
class Combine a where
  combine :: a -> a -> a
  combine3 :: a -> a -> a -> a
  combine3 x y z = combine x (combine y z)

Instance hierarchy

class Check a where
  check :: a -> Bool

instance Check Int where
  check x = x > 0
checkAll :: Check a => [a] -> Bool
checkAll = and (map check xs)
instance Check a => Check [a] where
  check xs = and (map check xs)
    • No instance for (Check a) arising from a use of ‘check’
      Possible fix:
        add (Check a) to the context of the instance declaration

Class hierarchy

class Foo a where
  foo :: a -> Int
class Foo a => Bar a where
  bar :: a -> a -> Int
  bar x y = foo x + foo y

Standard type classes: Eq, Ord

class  Eq a  where
      (==), (/=)  ::  a -> a -> Bool

      x /= y  = not (x == y)
      x == y  = not (x /= y)
class  (Eq a) => Ord a  where
  compare              :: a -> a -> Ordering
  (<), (<=), (>=), (>) :: a -> a -> Bool
  max, min             :: a -> a -> a

  compare x y | x == y    = EQ
              | x <= y    = LT
              | otherwise = GT

  x <= y  = compare x y /= GT
  x <  y  = compare x y == LT
  x >= y  = compare x y /= LT
  x >  y  = compare x y == GT

  max x y | x <= y    =  y
          | otherwise =  x
  min x y | x <= y    =  x
          | otherwise =  y

Example: the pair type

data Pair a = MkPair a a
  deriving Show

instance Eq a => Eq (Pair a) where
  MkPair a b == MkPair c d  = a==c && b==d

instance Ord a => Ord (Pair a) where
  MkPair a b <= MkPair c d
     | a<c       = True
     | a>c       = False
     | otherwise = b<=d
*Main> (MkPair 1 2) < (MkPair 2 3)
True
*Main> (MkPair 1 2) > (MkPair 2 3)
False
*Main> compare (MkPair 1 2) (MkPair 2 3)
LT

Standard type classes: Num

class  (Eq a, Show a) => Num a  where
    (+), (-), (⋆)  :: a -> a -> a
    negate         :: a -> a
    abs, signum    :: a -> a
    fromInteger    :: Integer -> a

More standard type classes

Haskell 2010 type classes

Haskell 2010 type classes

deriving

All sorts of maps

mapList :: (a->b) -> [a] -> [b]
mapList _ [] = []
mapList f (x:xs) = f x : mapList f xs
mapMaybe :: (a->b) -> Maybe a -> Maybe b
mapMaybe _ Nothing = Nothing
mapMaybe f (Just x) = Just (f x)
data Tree a = Leaf | Node a (Tree a) (Tree a)

mapTree _ Leaf = Leaf
mapTree f (Node x l r) = Node (f x) (mapTree f l) (mapTree f r)

All sorts of functors

class Functor f where
  fmap :: (a->b) -> f a -> f b
instance Functor Maybe where
  fmap _ Nothing = Nothing
  fmap f (Just x) = Just (f x)
instance Functor Tree where
  fmap _ Leaf = Leaf
  fmap f (Node x l r) = Node (f x) (mapTree f l) (mapTree f r)

List is a functor too

instance Functor [] where
  fmap _ [] = []
  fmap f (x:xs) = f x : fmap f xs

How does Haskell work?

not True = False
not False = True
map f [] = []
map f (x:xs) = f x : map f xs
length [] = 0
length (x:xs) = 1+length xs
length (map not (True:False:[]))
  ==> length (False:True:[])
  ==> 2
length (map not (True:False:[]))
  ==> length (not True : map not (False:[]))
  ==> 1 + length (map not (False:[]))
  ==> 1 + length (not False : map not ([]))
  ==> 1 + 1 + length (map not [])
  ==> 1 + 1 + length []
  ==> 1 + 1 + 0
  ==> 2

A word about sharing

f :: Int -> Int
f i = if i>10 then 10 else i
                  _______shared________
                 |                     |
f (1+1) ==> if (1+1)>10 then 10 else (1+1)
        ==> if 2>10 then 10 else 2
        ==> if False then 10 else 2
        ==> 2

Pretty normal forms

case x of Foo y -> ...
          Bar w z -> ...

Detailed example 1

not :: Bool -> Bool
not True = False
not False = True

(||) :: Bool -> Bool -> Bool
True || _ = True
_    || x = x

even x  =  x == 0  ||  not (even (x-1))
even' x =  not (even (x-1))  ||  x == 0

Detailed example 2

    head (filter (>100) (map (3^) [0..]))
==> head (filter (>100) (map (3^) (0:[1..])))
==> head (filter (>100) ((3^0) : map (3^) [1..]))
==> head (filter (>100) (1 : map (3^) [1..]))     -- (>100) forces 3^0
==> head (filter (>100) (map (3^) (1:[2..])))
==> head (filter (>100) ((3^1) : map (3^) [2..]))
==> head (filter (>100) (3 : map (3^) [2..]))
==> head (filter (>100) (map (3^) [2..]))
-- taking bigger steps now
==> head (filter (>100) (9 : map (3^) [3..]))
==> head (filter (>100) (27 : map (3^) [4..]))
==> head (filter (>100) (81 : map (3^) [5..]))
==> head (filter (>100) (243 : map (3^) [6..]))
==> head (243 : filter (>100) (map (3^) [6..]))
==> 243

Lecture 6: A Monoid in the Category of Problems

Contents

Example 1: Maybes

lookup :: (Eq a) => a -> [(a, b)] -> Maybe b
increase :: Eq a => a -> Int -> [(a,Int)] -> Maybe [(a,Int)]
increase key val assocs =
  case lookup key assocs
  of Nothing -> Nothing
     Just x -> if (val < x)
                then Nothing
                else Just ((key,val) : delete (key,x) assocs)

Example 1: continued

(?>) :: Maybe a -> (a -> Maybe b) -> Maybe b
Nothing ?> _ = Nothing   -- if we failed, don't even bother running the next step
Just x  ?> f = f x       -- otherwise run the next step

increase key val assocs =
    lookup key assocs ?>
    check ?>
    mk
  where check x
           | x < val   = Nothing
           | otherwise = Just x
        mk x = Just ((key,val) : delete (key,x) assocs)

Example 2: more Maybes

(?>) :: Maybe a -> (a -> Maybe b) -> Maybe b
Nothing ?> _ = Nothing   -- if we failed, don't even bother running the next step
Just x  ?> f = f x       -- otherwise run the next step

safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead (x:xs) = Just x

safeTail :: [a] -> Maybe [a]
safeTail [] = Nothing
safeTail (x:xs) = Just xs

safeThird xs = safeTail xs ?> safeTail ?> safeHead

safeNth 0 xs = safeHead xs
safeNth n xs = safeTail xs ?> safeNth (n-1)
safeThird [1,2,3,4]
  ==> Just 3
safeThird [1,2]
  ==> Nothing
safeNth 5 [1..10]
  ==> Just 6
safeNth 11 [1..10]
  ==> Nothing

Example 3: Logging

-- Logger definition
data Logger a = Logger [String] a  deriving Show
getVal (Logger _ a) = a
getLog (Logger s _) = s

-- Primitive operations:
nomsg x = Logger [] x        -- a value, no message
annotate s x = Logger [s] x  -- a value and a message
msg s = Logger [s] ()        -- just a message

(#>) :: Logger a -> (a -> Logger b) -> Logger b
Logger la a #> f = let Logger lb b = f a  -- feed value to next step
                   in Logger (la++lb) b   -- bundle result with all messages

-- compute the expression 2*(x^2+1)
compute x =
  annotate "^2" (x*x)
  #>
  \x -> annotate "+1" (x+1)
  #>
  \x -> annotate "*2" (x*2)
compute 3
  ==> Logger ["^2","+1","*2"] 20

Example 3: continued

data Logger a = Logger [String] a  deriving Show

nomsg x = Logger [] x        -- a value, no message
annotate s x = Logger [s] x  -- a value and a message
msg s = Logger [s] ()        -- just a message

(#>) :: Logger a -> (a -> Logger b) -> Logger b
Logger la a #> f = let Logger lb b = f a  -- feed value to next step
                   in Logger (la++lb) b   -- bundle result with all messages

-- sometimes you don't need the previous value:
(##>) :: Logger a -> Logger b -> Logger b
Logger la _ ##> Logger lb b = Logger (la++lb) b

filterLog :: (Eq a, Show a) => (a -> Bool) -> [a] -> Logger [a]
filterLog f [] = nomsg []
filterLog f (x:xs)
   | f x       = msg ("keeping "++show x) ##> filterLog f xs #> (\xs' -> nomsg (x:xs'))
   | otherwise = msg ("dropping "++show x) ##> filterLog f xs
filterLog (>0) [1,-2,3,-4,0]
  ==> Logger ["keeping 1","dropping -2","keeping 3","dropping -4","dropping 0"] [1,3]

Example 4: keeping state

data Tree a = Leaf | Node a (Tree a) (Tree a)

number tree = t
  where (t,i) = number' 0 tree

number' :: Int -> Tree a -> (Tree Int, Int)
number' i Leaf = (Leaf,i)
number' i (Node _ l r) = (Node i' numberedL numberedR, i'')
  where (numberedL, i')  = number' i l
        (numberedR, i'') = number' (i'+1) r
number (Node 0 (Node 0 (Node 0 Leaf Leaf) Leaf) (Node 0 (Node 0 Leaf Leaf) (Node 0 Leaf Leaf)))
     ==> Node 2 (Node 1 (Node 0 Leaf Leaf) Leaf) (Node 4 (Node 3 Leaf Leaf) (Node 5 Leaf Leaf))

Example 4: … with chaining

data Counter a = Counter (Int -> (a,Int))

runCounter :: Counter a -> Int -> (a,Int)
runCounter (Counter f) s = f s

-- Get the current state, increment the state by one
getAndIncrement :: Counter Int
getAndIncrement = Counter (\i -> (i,i+1))

-- Produce the given value, don't change the state
noChange :: a -> Counter a
noChange x = Counter (\i -> (x,i))

chain :: Counter a -> (a -> Counter b) -> Counter b
chain op f = Counter g
  where g i = let (value, new) = runCounter op i
                  op2 = f value
              in runCounter op2 new

-- query the counter two times
example = getAndIncrement
          `chain`
          (\val1 -> getAndIncrement `chain` (\val2 -> noChange (val1, val2)))
runCounter example 2 ==> ((2,3),4)

Example 4: continued

number :: Tree a -> Tree Int
number tree = t
  where (t,_) = runCounter (number' tree) 0

number' :: Tree a -> Counter (Tree Int)
number' Leaf = noChange Leaf
number' (Node _ l r) =
  number' l
  `chain`
  (\numberedL -> getAndIncrement
                 `chain`
                 (\i -> number' r
                        `chain`
                        (\numberedR -> noChange (Node i numberedL numberedR))))
number (Node 0 (Node 0 (Node 0 Leaf Leaf) Leaf) (Node 0 (Node 0 Leaf Leaf) (Node 0 Leaf Leaf)))
     ==> Node 2 (Node 1 (Node 0 Leaf Leaf) Leaf) (Node 4 (Node 3 Leaf Leaf) (Node 5 Leaf Leaf))

Finally: Monad

(?>) :: Maybe a -> (a -> Maybe b) -> Maybe b
(#>) :: Logger a -> (a -> Logger b) -> Logger b
chain :: Counter a -> (a -> Counter b) -> Counter b
class Monad m where
  (>>=) :: m a -> (a -> m b) -> m b
  -- lift a normal value into the monad
  return :: a -> m a
  -- simpler chaining (remember ##>!)
  (>>) :: m a -> m b -> m b
  a >> b  =  a >>= \_x -> b
  -- failed computation
  fail :: String -> m a

Don’t panic!

fmap :: Functor f :: (a->b) -> f a -> f b
(>>=) :: Monad m => m a -> (a -> m b) -> m b
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
Nothing >>= _ = Nothing  -- if we failed, don't even bother running the next step
Just x  >>= f = f x      -- otherwise run the next step

Maybe is a Monad!

instance  Monad Maybe  where
    (Just x) >>= k      = k x
    Nothing  >>= _      = Nothing

    (Just _) >>  k      = k
    Nothing  >>  _      = Nothing

    return              = Just
    fail _              = Nothing
Just 1 >>= \x -> return (x+1)
  ==> Just 2
Just "HELLO" >>= \x -> return (length x) >>= \x -> return (x+1)
  ==> Just 6
Just "HELLO" >>= \x -> Nothing
  ==> Nothing
Just "HELLO" >> Just 2
  ==> Just 2
Just 2 >> Nothing
  ==> Nothing

Maybe is a Monad! continued

increase key val assocs =
    lookup key assocs >>=
    check >>=
    mk
  where check x
           | val < x   = fail ""
           | otherwise = return x
        mk x = return ((key,val) : delete (key,x) assocs)

The return of do

f = op1 >>= continue
  where continue  x   = op2 >> op3 >>= continue2 x
        continue2 x y = op4 >> op5 x y
f = op1 >>= (\x ->
               op2 >>
               op3 >>= (\y ->
                          op4 >>
                          op5 x y))
f = op1 >>= \x ->
    op2 >>
    op3 >>= \y ->
    op4 >>
    op5 x y

The return of do: continued

f = op1 >>= \x ->
    op2 >>
    op3 >>= \y ->
    op4 >>
    op5 x y
f = do x <- op1
       op2
       y <- op3
       op4
       op5 x y

The return of do: continued

do x <- op a       ~~~>       op a >>= \x -> do ...
   ...
do op a            ~~~>       op a >> do ...
   ...
do let x = expr    ~~~>       let x = expr in do ...
   ...
do finalOp         ~~~>       finalOp

More do examples

safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead (x:xs) = Just x

safeTail :: [a] -> Maybe [a]
safeTail [] = Nothing
safeTail (x:xs) = Just xs

safeNth 0 xs = safeHead xs
safeNth n xs = do t <- safeTail xs
                  safeNth (n-1) t
increase key val assocs = do
    val <- lookup key assocs
    check val
    return ((key,val) : delete (key,x) assocs)
  where check x
           | val < x   = fail ""
           | otherwise = return x

Logger is a Monad!

data Logger a = Logger [String] a  deriving Show

instance Monad Logger where
  return x = Logger [] x
  Logger la a >>= f = Logger (la++lb) b
    where Logger lb b = f a

msg s = Logger [s] ()

compute x = do
  a <- annotate "^2" (x*x)
  b <- annotate "+1" (a+1)
  annotate "*2" (b*2)

filterLog :: (Eq a, Show a) => (a -> Bool) -> [a] -> Logger [a]
filterLog f [] = return []
filterLog f (x:xs)
   | f x       = do msg ("keeping "++show x)
                    xs' <- filterLog f xs
                    return (x:xs')
   | otherwise = do msg ("dropping "++show x)
                    filterLog f xs
filterLog (>0) [1,-2,3,-4,0]
  ==> Logger ["keeping 1","dropping -2","keeping 3","dropping -4","dropping 0"] [1,3]

The State Monad

data State s a = State (s -> (a,s))

runState (State f) s = f s

put :: s -> State s ()
put state = State (\_state -> ((),state))

get :: State s s
get = State (\state -> (state,state))

modify :: (s -> s) -> State s ()
modify f = State (\state -> ((), f state))

instance Monad (State s) where
  return x = State (\s -> (x,s))

  op >>= f = State h
    where h state0 = let (val,state1) = runState op state0
                         op2 = f val
                     in runState op2 state1

State example 1

add :: Int -> State Int ()
add i = do old <- get
           put (old+i)
runState (add 1 >> add 3 >> add 5 >> add 6) 0
  ==> ((),15)
oo = do add 3
        value <- get
        add 1000
        put (value + 1)
        return value
runState oo 1
(4,5)

State example 2

remember :: a -> State [a] ()
remember x = modify (x:)

valuesAfterZero xs = runState (go xs) []
  where go :: [a] -> State [a] ()
        go (0:y:xs) = do remember y
                         go (y:xs)
        go (x:xs) = go xs
        go [] = return ()
valuesAfterZero [0,1,2,3,0,4,0,5,0,0,6]
  ==> ((),[6,0,5,4,1])

State example 3

parensMatch xs = v
  where (v,_) = runState (matcher xs) 0

matcher :: String -> State Int Bool
matcher [] = do s <- get
                return (s==0)
matcher (c:cs) = do case c of '(' -> modify (+1)
                              ')' -> modify (-1)
                              _ -> return ()
                    s <- get
                    if (s<0) then return False else matcher cs
number :: Tree a -> Tree Int
number tree = t
  where (t,_) = runState (numberer tree) 0
numberer :: Tree a -> State Int (Tree Int)
numberer Leaf = return Leaf
numberer (Node _ l r) = do numberedL <- numberer l
                           i <- get
                           put (i+1)
                           numberedR <- numberer r
                           return (Node i numberedL numberedR)

The return of mapM

when :: Monad m => Bool -> m () -> m ()        -- conditional operation
unless :: Monad m => Bool -> m () -> m ()      -- same but flipped
replicateM :: Monad m => Int -> m a -> m [a]   -- do something many times
replicateM_ :: Monad m => Int -> m a -> m ()   -- same, but ignore the results
mapM :: Monad m => (a -> m b) -> [a] -> m [b]  -- do something on a list's elements
mapM_ :: Monad m => (a -> m b) -> [a] -> m ()  -- same, but ignore the results
forM  :: Monad m => [a] -> (a -> m b) -> m [b] -- mapM but arguments reversed
forM_ :: Monad m => [a] -> (a -> m b) -> m ()  -- same, but ignore the results
safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead (x:xs) = Just x
firsts :: [[a]] -> Maybe [a]
firsts xs = mapM safeHead xs
firsts [[1,2,3],[4,5],[6]] ==> Just [1,4,6]
firsts [[1,2,3],[],[6]]    ==> Nothing
let op = modify (+1) >> get
    ops = replicateM 4 op
in runState ops 0
  ==> ([1,2,3,4],4)

The return of mapM: continued

sfilter :: (a -> Bool) -> [a] -> [a]
sfilter f xs = reverse $ execState (go xs) []
  where go xs = mapM_ maybePut xs
        maybePut x = when (f x) (modify (x:))
sfilter even [1,2,3,4,5]
  ==> [2,4]

Polymorphic Monad operations

mywhen b op = if b then op else return ()

mymapM_ op [] = return ()
mymapM_ op (x:xs) = do op x
                       mymapM_ op xs
*Main> :t mywhen
mywhen :: (Monad m) => Bool -> m () -> m ()
*Main> :t mymapM_
mymapM_ :: (Monad m) => (t -> m a) -> [t] -> m ()

Monads are Functors

liftM :: Monad m => (a->b) -> m a -> m b
liftM f op = do x <- op
                return (f x)
liftM sort $ firsts [[4,6],[2,1,0],[3,3,3]]
  ==> Just [2,3,4]
fmap :: Functor f => (a->b) -> f a -> f b
fmap sort $ firsts [[4,6],[2,1,0],[3,3,3]]
  ==> Just [2,3,4]

One more Monad

[1,2,3] >>= \x -> [-x,x]
  ==> [-1,1,-2,2,-3,3]
findSum :: [Int] -> Int -> [(Int,Int)]
findSum xs k = do a <- xs
                  b <- xs
                  if (a+b==k) then [(a,b)] else []
findSum :: [Int] -> Int -> [(Int,Int)]
findSum xs k = do a <- xs
                  b <- xs
                  if (a+b==k) then return (a,b) else fail ""
findSum [1,2,3,4,5] 5
  ==> [(1,4),(2,3),(3,2),(4,1)]

One more example

substrings :: String -> [String]
substrings xs = do i <- [0..length xs - 1]
                   let maxlen = length xs - i
                   j <- [1..maxlen]
                   return $ take j $ drop i $ xs

palindromesIn :: String -> [String]
palindromesIn xs = do s <- substrings xs
                      if (s==reverse s) then return s else fail ""

longestPalindrome xs = head . sortBy f $ palindromesIn xs
  where f s s' = compare (length s') (length s)  -- longer is smaller
longestPalindrome "aabbacddcaca"
  ==> "acddca"

One more implementation

instance Monad [] where
  return x = [x]                  -- an operation that produces one value
  lis >>= f = concat (map f lis)  -- compute f for all values, combine the results

Oh right, IO

instance Monad IO where

Monads: summary

Where to go from here?

Where to go from here: topics