You A Haskell Chapter 6
Higher Order Functions
Haskell functions can take functions as parameters and return functions as values. A function that does so is called a higher order function.
Curried Functions
Functions in haskell only take one parameter — any functions that take more are actually curried functions.
For example, taking the max of two numbers:
max 4 5
is just like this:
(max 4) 5
remember the type of max is this:
max :: Ord a => a -> a -> a
For this example:
multThree :: (Num a) => a -> a -> a -> a
multThree x y z = x * y * z
we can compose this function with some other ones like so:
ghci> let multTwoWithNine = multThree 9
ghci> multTwoWithNine 2 3
54
ghci> let multWithEighteen = multTwoWithNine 2
ghci> multWithEighteen 10
180
We can compare any number with 100 like so:
compareWithHundred :: (Num a, Ord a) => a -> Ordering
compareWithHundred x = compare 100 x
Infix functions can be partially applied using sections.
divideByTen :: (Floating a) => a -> a
divideByTen = (/10)
What about a function that checks if a character supplied to it is uppercase?
isUpperAlphanum :: Char -> Bool
isUpperAlpanum = (`elem` ['A'..'Z'])
Let’s create a function that applies a function twice
applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)
and let’s use it
ghci> applyTwice (+3) 10
16
ghci> applyTwice (++ "HEY"
"HEY HAHA HAHA"
ghci> applyTwice ("HAHA " ++) "HEY"
"HAHA HAHA HEY"
ghci> applyTwice (multThree 2 2) 9
144
ghci> applyTwice (3:) [1]
[3,3,1]
Let’s make zipWith, which takes a function and two lists. it applies the function to both of the lists and returns a new list.
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
ghci> zipWith' (+) [4,2,5,6] [2,6,2,3]
[6,8,7,9]
ghci> zipWith' max [6,3,2,1] [7,3,1,5]
[7,3,2,5]
ghci> zipWith' (++) ["foo ", "bar ", "baz "] ["fighters", "hoppers", "aldrin"]
["foo fighters","bar hoppers","baz aldrin"]
ghci> zipWith' (*) (replicate 5 2) [1..]
[2,4,6,8,10]
ghci> zipWith' (zipWith' (*)) [1,2,3],[3,5,6],[2,3,4]] [3,2,2],[3,4,5],[5,4,3]]
[3,4,6],[9,20,30],[10,12,12]]
we can also implement flip
, which takes a function and returns a
function that is like the function provided, just with the two arguments
flipped.
flip' :: (a -> b -> c) -> (b -> a -> c)
flip' f = g
where g x y = f y x
We can simplify this by noticing that functions are curried:
flip' :: (a -> b -> c) -> b -> a -> c
flip' f y x = f x y
ghci> flip' zip [1,2,3,4,5] "hello"
[('h',1),('e',2),('l',3),('l',4),('o',5)]
ghci> zipWith (flip' div) [2,2..] [10,8,6,4,2]
[5,4,3,2,1]
Maps and Filters
map
takes a function and a list and applies that function to every
element in the list.
map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs
ghci> map (+3) [1,5,3,1,6]
[4,8,6,4,9]
ghci> map (++ ["BIFF", "BANG", "POW"]
["BIFF!","BANG!","POW!"]
ghci> map (replicate 3) [3..6]
[3,3,3],[4,4,4],[5,5,5],[6,6,6]]
ghci> map (map (^2)) [1,2],[3,4,5,6],[7,8]]
[1,4],[9,16,25,36],[49,64]]
ghci> map fst [(1,2),(3,5),(6,3),(2,6),(2,5)]
[1,3,6,2,2]
filter
takes a predicate function and if the function evaluates to
true, keeps that element.
filter :: (a -> Bool) -> [a] -> [a]
filter _ [] = []
filter p (x:xs)
| p x = x : filter p xs
| _ = filter p xs
ghci> filter (>3) [1,5,3,2,1,6,4,3,2,1]
[5,6,4]
ghci> filter (==3) [1,2,3,4,5]
[3]
ghci> filter even [1..10]
[2,4,6,8,10]
ghci> let notNull x = not (null x) in filter notNull [1,2,3],[],[3,4,5],[2,2],[],[],[]]
[1,2,3],[3,4,5],[2,2]]
ghci> filter (`elem` ['a'..'z']) "u LaUgH aT mE BeCaUsE I aM diFfeRent"
"uagameasadifeent"
ghci> filter (`elem` ['A'..'Z']) "i lauGh At You BecAuse u r aLL the Same"
"GAYBALLS"
Here’s the previous chapter’s quicksort with Filters
quicksort :: (Ord a) => [a] -> a
quicksort [] = []
quicksort (x:xs) =
let smaller = quicksort (filter (<= x) xs)
larger = quicksort (filter (>x) xs)
in smaller ++ [x] ++ larger
Finding the largest number under 100,000 that’s divisible by 3829.
largestDivisible :: (Integral a) => a
largestDivisible = head (filter p [100000,99999..])
where p x = x `mod` 3829 == 0
find the sum of all odd squares that are smaller than 10,000.
ghci> sum (takeWhile (<10000) (filter odd (map (^2) [1..])))
166650
Lambdas
Lambdas are expressions that can take any number of arguments and be passed to any function. When applied, they return a value.
This map sums up the pairs here and returns a list:
map ((a,b) -> a + b) [(1,2),(3,5),(6,3),(2,6),(2,5)]
Folds
Let’s implement sum using a fold:
sum' :: (Num a) => [a] -> a
sum' xs = foldl (acc x -> acc + x) 0 xs
ghci> sum' [3,5,2,1]
11
If we apply the addition function to all arguments however, we can do this more succinctly:
sum' :: (Num a) => [a] -> a
sum' = foldl (+) 0
foldr
works the same way, except it approaches from the right hand
side:
map' :: (a -> b) -> [a] -> [b]
map' f xs = foldr (x acc -> f x : acc) [] xs
One caveat why foldr
exists is that it works on infinite lists, but
foldl
will never terminate.
foldl1
uses the first element as the accumulator: foldr1
uses the
last element as the accumulator:
sum' :: (Num a) => [a] -> a
sum' = foldl1 (+)
Here are some library functions using folds
maximum' :: (Ord a) => [a] -> a
maximum' = foldr1 (x acc -> if x > acc then x else acc)
reverse' :: [a] -> [a]
reverse' = foldl (acc x -> x : acc) []
product' :: (Num a) => [a] -> a
product' = foldr1 (*)
filter' :: (a -> Bool) -> [a] -> [a]
filter' p = foldr (x acc -> if p x then x : acc else acc) []
head' :: [a] -> a
head' = foldr1 (x _ -> x)
last' :: [a] -> a
last' = foldl1 (_ x -> x)
scanl
and scanr
are similar to foldl
and foldr
, but they report
their intermediate states:
ghci> scanl (+) 0 [3,5,2,1]
[0,3,8,10,11]
ghci> scanr (+) 0 [3,5,2,1]
[11,8,3,1,0]
ghci> scanl1 (acc x -> if x > acc then x else acc) [3,4,5,3,7,9,2,1]
[3,4,5,5,7,9,9,9]
ghci> scanl (flip (:)) [] [3,2,1]
[],[3],[2,3],[1,2,3]]
Function application can be done with $
, which evaluates the right
hand side of the equation before the left.
Instead of sqrt (3 + 4 + 9)
we can write sqrt $ 3 + 4 + 9
.
Function composition can be done with the .
operator:
Instead of:
ghci> map (x -> negate (abs x)) [5,-3,-6,7,-3,2,-19,24]
[-5,-3,-6,-7,-3,-2,-19,-24]
We can do this:
ghci> map (negate . abs) [5,-3,-6,7,-3,2,-19,24]
[-5,-3,-6,-7,-3,-2,-19,-24]
Prev: learn-you-a-haskell-chapter-5 Next: learn-you-a-haskell-chapter-7