Higher Order Functions

{{% section %}}

Higher Order Functions

@wusyong, 吳昱緯


Higher order functions has at least one of...

  • takes one or more functions as arguments
  • returns a function as its result

Higher order functions are indispensable if we want to define computations by defining what stuff is instead of defining steps that change some state and maybe looping them

{{% /section %}}


{{% section %}}

Curried functions

  • Every function in Haskell officially only takes one parameter.
  • All the functions that accepted several parameters so far have been curried functions.
ghci> max 4 5  
5  
ghci> (max 4) 5  
5  

Function Application

  • max :: (Ord a) => a -> a -> a == max :: (Ord a) => a -> (a -> a)
  • max takes an a and returns a function that takes an a and returns an a

Partially Applied Function

Partial application (calling functions with too few parameters) is a neat way to create functions on the fly.

multThree :: (Num a) => a -> a -> a -> a  
multThree x y z = x * y * z  

ghci> let multTwoWithNine = multThree 9  
ghci> multTwoWithNine 2 3  
54  
ghci> let multWithEighteen = multTwoWithNine 2  
ghci> multWithEighteen 10  
180  

Partially Applied Function

compareWithHundred :: (Num a, Ord a) => a -> Ordering  
compareWithHundred x = compare 100 x  

While compare has type of (Ord a) => a -> (a -> Ordering)

compareWithHundred :: (Num a, Ord a) => a -> Ordering  
compareWithHundred = compare 100  

Infix functions work too

To section an infix function, simply surround it with parentheses and only supply a parameter on one side.

divideByTen :: (Floating a) => a -> a  
divideByTen = (/10)

isUpperAlphanum :: Char -> Bool  
isUpperAlphanum = (`elem` ['A'..'Z'])

(-4) means minus 4 for convenience. Use (subtract 4) for infix section instead.


Without let to bind the name

ghci> multThree 3 4  
<interactive>:1:0:  
    No instance for (Show (t -> t))  
        arising from a use of `print' at <interactive>:1:0-12  
    Possible fix: add an instance declaration for (Show (t -> t))  
    In the expression: print it  
    In a 'do' expression: print it  

{{% /section %}}


{{% section %}}

Some higher-orderism is in order

Functions can take functions as parameters and also return functions.

applyTwice :: (a -> a) -> a -> a  
applyTwice f x = f (f x)

Parentheses is now madatory here since they indicate first parameter is a function.


applyTwice

ghci> applyTwice (+3) 10  
16  
ghci> applyTwice (++ " HAHA") "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]

zipWith

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

flip

flip' :: (a -> b -> c) -> (b -> a -> c)  
flip' f = g  
    where g x y = f y x  

But -> is right associative by default.

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] 

{{% /section %}}


{{% section %}}

Maps and filters


map

map takes a function and a list and applies that function to every element in the list, producing a new 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

filter is a function that takes a predicate and a list and then returns the list of elements that satisfy the predicate.

filter :: (a -> Bool) -> [a] -> [a]  
filter _ [] = []  
filter p (x:xs)   
    | p x       = x : filter p xs  
    | otherwise = 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"

Quick sort with filter

quicksort :: (Ord a) => [a] -> [a]    
quicksort [] = []    
quicksort (x:xs) =     
    let smallerSorted = quicksort (filter (<=x) xs)  
        biggerSorted = quicksort (filter (>x) xs)   
    in  smallerSorted ++ [x] ++ biggerSorted

largestDivisible

largestDivisible :: (Integral a) => a  
largestDivisible = head (filter p [100000,99999..])  
    where p x = x `mod` 3829 == 0

takeWhile

takeWhile takes a predicate and a list and then goes from the beginning of the list and returns its elements while the predicate holds true. Becasue of Haskell's property of laziness, list won't actually map and filter it right away. takeWhile forces the filtering and mapping to occur.

ghci> sum (takeWhile (<10000) (filter odd (map (^2) [1..]))) 
ghci> sum (takeWhile (<10000) [n^2 | n <- [1..], odd (n^2)])  
166650

Collatz sequences

chain :: (Integral a) => a -> [a]  
chain 1 = [1]  
chain n  
    | even n =  n:chain (n `div` 2)  
    | odd n  =  n:chain (n*3 + 1)

ghci> chain 10  
[10,5,16,8,4,2,1]  
ghci> chain 1  
[1]  
ghci> chain 30  
[30,15,46,23,70,35,106,53,160,80,40,20,10,5,16,8,4,2,1]

Collatz sequences

numLongChains :: Int  
numLongChains = length (filter isLong (map chain [1..100]))  
    where isLong xs = length xs > 15

Let's used in partially applied functions

ghci> let listOfFuns = map (*) [0..]  
ghci> (listOfFuns !! 4) 5  
20

{{% /section %}}


{{% section %}}

Lambdas

Lambdas are basically anonymous functions that are used because we need some functions only once.


Lambdas: \

numLongChains :: Int  
numLongChains = length (filter (\xs -> length xs > 15) (map chain [1..100]))

ghci> zipWith (\a b -> (a * 30 + 3) / b) [5,4,3,2,1] [1,2,3,4,5]  
[153.0,61.5,31.0,15.75,6.6]

ghci> map (\(a,b) -> a + b) [(1,2),(3,5),(6,3),(2,6),(2,5)]  
[3,8,9,8,7]

Lamda as parameters

These two are the same

addThree :: (Num a) => a -> a -> a -> a  
addThree x y z = x + y + z 

addThree :: (Num a) => a -> a -> a -> a  
addThree = \x -> \y -> \z -> x + y + z

Sometimes it is more readable

flip' :: (a -> b -> c) -> b -> a -> c  
flip' f = \x y -> f y x  

{{% /section %}}


{{% section %}}

Only folds and horses

A fold takes a binary function, a starting value (I like to call it the accumulator) and a list to fold up.


left fold: foldl

sum' :: (Num a) => [a] -> a  
sum' xs = foldl (\acc x -> acc + x) 0 xs

ghci> sum' [3,5,2,1]  
11

Or make it curried

sum' :: (Num a) => [a] -> a  
sum' = foldl (+) 0

left fold: foldl

elem' :: (Eq a) => a -> [a] -> Bool  
elem' y ys = foldl (\acc x -> if x == y then True else acc) False ys  

right fold: foldr

Parameters order is reversed.

map' :: (a -> b) -> [a] -> [b]  
map' f xs = foldr (\x acc -> f x : acc) [] xs

foldl1 & foldr1

The first (or last) element of the list to be the starting value.

maximum' :: (Ord a) => [a] -> a  
maximum' = foldr1 (\x acc -> if x > acc then x else acc)  
  
product' :: (Num a) => [a] -> a  
product' = foldr1 (*)  
  
head' :: [a] -> a  
head' = foldr1 (\x _ -> x)  
  
last' :: [a] -> a  
last' = foldl1 (\_ x -> x)

scanl & scanr

Report all the intermediate accumulator states in the form of a list.

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

scanl & scanr

Report all the intermediate accumulator states in the form of a list.

sqrtSums :: Int  
sqrtSums = length (takeWhile (<1000) (scanl1 (+) (map sqrt [1..]))) + 1

ghci> sqrtSums  
131  
ghci> sum (map sqrt [1..131])  
1005.0942035344083  
ghci> sum (map sqrt [1..130])  
993.6486803921487

{{% /section %}}


{{% section %}}

Function application with $

  • $ is called function application but with lowest precedence.
  • Usually f a b c is the same as ((f a) b) c) because it's left-associative.
  • It becomes right-associative with $
($) :: (a -> b) -> a -> b  
f $ x = f x

$ functions

  • So sum (map sqrt [1..130]) == sum $ map sqrt [1..130]
  • sum $ map sqrt [1..130] == sqrt $ 3 + 4 + 9
  • sum (filter (> 10) (map (*2) [2..10])) == sum $ filter (> 10) $ map (*2) [2..10]
  • Since $ is function application, it can be treat as another function:
ghci> map ($ 3) [(4+), (10*), (^2), sqrt]  
[7.0,30.0,9.0,1.7320508075688772]

{{% /section %}}


{{% section %}}

Function composition

  • Haskell does function composition with the . function:
(.) :: (b -> c) -> (a -> b) -> a -> c  
f . g = \x -> f (g x)

Make function on the fly to pass to other function

ghci> map (\x -> negate (abs x)) [5,-3,-6,7,-3,2,-19,24]  
[-5,-3,-6,-7,-3,-2,-19,-24]
-- into
ghci> map (negate . abs) [5,-3,-6,7,-3,2,-19,24]  
[-5,-3,-6,-7,-3,-2,-19,-24] 

ghci> map (\xs -> negate (sum (tail xs))) [[1..5],[3..6],[1..7]]  
[-14,-15,-27]
-- into
ghci> map (negate . sum . tail) [[1..5],[3..6],[1..7]]  
[-14,-15,-27]

Work well with $

sum (replicate 5 (max 6.7 8.9))
sum . replicate 5 . max 6.7) 8.9
sum . replicate 5 . max 6.7 $ 8.9

replicate 100 (product (map (*3) (zipWith max [1,2,3,4,5] [4,5,6,7,8])))
replicate 100 . product . map (*3) . zipWith max [1,2,3,4,5] $ [4,5,6,7,8]

Define pointless style function

sum' :: (Num a) => [a] -> a     
sum' xs = foldl (+) 0 xs
sum' = foldl (+) 0
fn x = ceiling (negate (tan (cos (max 50 x))))
fn = ceiling . negate . tan . cos . max 50

Putting things together

oddSquareSum :: Integer  
oddSquareSum = sum (takeWhile (<10000) (filter odd (map (^2) [1..]))

oddSquareSum :: Integer  
oddSquareSum = sum . takeWhile (<10000) . filter odd . map (^2) $ [1..]

oddSquareSum :: Integer  
oddSquareSum =   
    let oddSquares = filter odd $ map (^2) [1..]  
        belowLimit = takeWhile (<10000) oddSquares  
    in  sum belowLimit