7. Higher Order Functions
7. Higher Order Functions
LANGUAGES
• The type declaration stays the same, because compare 100 returns a
function. Compare has a
• type of (Ord a) => a -> (a -> Ordering) and calling it with 100 returns a
(Num a, Ord a) => a -> Ordering.
• The additional class constraint sneaks up there because 100 is also part
of the Num typeclass.
MAKING INFIX FUNCTIONS TO BE PARTIALLY APPLIED
USING SECTIONS
To section an infix function, simply
divideByTen :: (Floating a) => a -> a surround it with parentheses and
only supply a parameter on one
divideByTen = (/10) side. That creates a function that
takes one parameter and then
ghci> divideByTen 200 applies it to the side that's missing
20.0 an operand.
• The first parameter is a function that takes two things and produces a third thing.
• They don't have to be of the same type, but they can.
• The second and third parameter are lists.
• The result is also a list.
• The first has to be a list of a's, because the joining function takes a's as its first
argument.
• The second has to be a list of b's, because the second parameter of the joining
function is of type b.
• The result is a list of c's.
EXECUTING ZIPWITH’ • A single higher order function can be used in very
versatile ways.
ghci> zipWith' (+) [4,2,5,6] [2,6,2,3] • Imperative programming usually uses constructs like
for loops, while loops, setting something to a
[6,8,7,9] variable, checking its state, etc. to achieve some
ghci> zipWith' max [6,3,2,1] [7,3,1,5] behaviour
a function.
and then wrap it around an interface, like
where g x y = f y x
--mysum.hs
mysum :: (Num a) => [a] -> a sum' :: (Num a) => [a] -> a
mysum [] = 0 sum' xs = foldl (\acc x -> acc + x) 0 xs
mysum (x:xs) = x + sum xs
• If we're mapping (+3) to [1,2,3] , we approach the list from the right side
• Take the last element, which is 3 and apply the function to it, which ends
up being 6
• Then, prepend it to the accumulator, which is was[]. 6:[] is [6] and that's
now the accumulator
• Apply (+3) to 2, that's 5 and we prepend (:) it to the accumulator
• The accumulator is now [5,6]
• Apply (+3) to 1 and prepend that to the accumulator and so the end
value is [4,5,6]
• Left fold version:
map' f xs = foldl (\acc x -> acc ++ [f x]) [] xs
• the ++ function is much more expensive than :, so we usually use right
folds when we're building up new lists from a list.
DIFFERENCE BETWEEN LEFT AND RIGHT FOLDS
• If you take an infinite list at some point and you fold it up from the right,
you'll eventually reach the beginning of the list.
• However, if you take an infinite list at a point and you try to fold it up
from the left, you'll never reach an end!
FOLDL1 AND FOLDR1
• The foldl1 and foldr1 functions work much like foldl and foldr, only you
don't need to provide them with an explicit starting value.
• They assume the first (or last) element of the list to be the starting value
and then start the fold with the element next to it.
• For Example: the sum function can be implemented as: sum = foldl1 (+) .
• Because they depend on the lists they fold up having at least one
element, they cause runtime errors if called with empty lists.
• foldl and foldr, on the other hand, work fine with empty lists.
• If the function doesn't make sense when given an empty list, you can
probably use a foldl1 or foldr1 to implement it.
SOME STANDARD 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)
FUNCTION APPLICATION WITH $
Looks like function
($) :: (a -> b) -> a -> b application but not
so !
f$x=fx
• sum (filter (> 10) (map (*2) [2..10])) can be written using $ as
• Mind the type declaration. f must take as its parameter a value that has
the same type as g's return value.
• Example: negate .(* 3) returns a function that takes a number, multiplies
it by 3 and then negates it
• One of the uses for function composition is making functions on the fly to
pass to other functions.
• Function composition is clearer and more concise than lambda
EXAMPLES
Using Lambda:
ghci> map (\x -> negate (abs x)) [5,-3,-6,7,-3,2,-19,24]
[-5,-3,-6,-7,-3,-2,-19,-24]
Using Lambda:
ghci> map (\xs -> negate (sum (tail xs))) [[1..5],[3..6],[1..7]]
[-14,-15,-27]
• Start by putting the last parameter of the innermost function after a $ and
then just composing all the other function calls, writing them without their
last parameter and putting dots between them.
• If the expression ends with three parentheses, chances are that if you
translate it into function composition, it'll have three composition
operators.
POINT FREE STYLE
fn x = ceiling (negate (tan (cos (max 50 x))))
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
THANK YOU