module HigherOrder where
import Prelude hiding ((++))
Polymorphic Functions (#polymorphism)
Polymorphic Data (#polymorphic-data-structures)
Bottling Computation Patterns (#bottling-computation-patterns)
Many functions are oblivious to the type of their arguments
doTwice f x = f (f x)
Works on many kinds of values
ghci> :type doTwice
doTwice :: (a -> a) -> a -> a
a
f :: a -> a
x
:: a`a
Many functions are oblivious to the type of their arguments
doTwice :: (a -> a) -> a -> a
doTwice f x = f (f x)
Int
valuesghci> doTwice (+1) 0
2
Type parameter a
is instantiated as Int
Many functions are oblivious to the type of their arguments
doTwice :: (a -> a) -> a -> a
doTwice f x = f (f x)
String
valuesghci> doTwice (++ "I don't care! ") "Isn't this neat ?"
"Isn't this neat ? I don't care! I don't care! "
Type parameter a
is instantiated as String
doTwice
is PolymorphicWorks on many kinds of values
doTwice :: (a -> a) -> a -> a
doTwice f x = f (f x)
Int
, String
...Works on many kinds of values
doTwice :: (a -> a) -> a -> a
doTwice f x = f (f x)
Reuse same doTwice
across different operators and data
... comes great responsibility!
ghci> (10 <) 12
True
Quiz: What is the value of?
ghci> doTwice (10 <) 100
... comes great responsibility!
ghci> doTwice (10 <) 100
doTwice :: (a -> a) -- operator
-> a -- input
-> a -- output
Operator should return same type as output ...
... but (10 <) :: Int -> Bool
Are you following so far ?
ex1 = doTwice doTwice
What is the type of ex1
?
What is the type parameter of doTwice
instantiated as?
Of course, you can also create polymorphic data types
data List a
= Nil -- empty list
| Cons a (List a) -- "cons" of a hd :: a and tl :: List a
also known as (defined in standard library)
data [a]
= [] -- empty list
| (:) a [a] -- "cons" of a hd :: a and tl :: List a
Polymorphic functions and data go hand in hand ...
len :: [a] -> Int
len [] = 0
len (_:xs) = 1 + len xs
or our friends from last time
(++) :: [a] -> [a] -> [a]
head :: [a] -> a
tail :: [a] -> [a]
Note:
toUpperString [] = []
toUpperString (c:cs) = (??? c) : toUpperString cs
Er, what to put in ???
Let's ask Hoogle!
type XY = (Double, Double)
type Polygon = [XY]
shiftPoly d [] = []
shiftPoly d (xy:xys) = shiftXY d xy : shiftPoly d xys
shiftXY (dx, dy) (x, y) = (x + dx, y + dy)
toUpperString
and shiftPoly
are 93% same!shiftPoly d [] = []
shiftPoly d (xy:xys) = shiftXY d xy : shiftPoly d xys
toUpperString [] = []
toUpperString (c:cs) = (toUpper c) : toUpperString cs
toUpperString
and shiftPoly
are 93% same!shiftPoly d [] = []
shiftPoly d (xy:xys) = shiftXY d xy : shiftPoly d xys
toUpperString [] = []
toUpperString (c:cs) = (toUpper c) : toUpperString cs
map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = (f x) : (map f xs)
map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = (f x) : (map f xs)
toUpperString = map toUpper
shiftPoly = map shiftXY
Quiz: Dude, where are the parameters?
listAdd [] = 0
listAdd (x:xs) = x + (listAdd xs)
listMul [] = 1
listMul (x:xs) = x * (listMul xs)
listAdd [] = 0
listAdd (x:xs) = x + (listAdd xs)
listMul [] = 1
listMul (x:xs) = x * (listMul xs)
Quiz: What are differences between listAdd
and listMul
?
foldr
listAdd [] = 0
listAdd (x:xs) = x + (listAdd xs)
listMul [] = 1
listMul (x:xs) = x * (listMul xs)
foldr op base [] = base
foldr op base (x:xs) = x `op` (foldr op base xs)
listAdd = foldr (+) 0 -- op = (+), base = 0
listMul = foldr (*) 1 -- op = (*), base = 1
foldr
foldr op base [] = base
foldr op base (x:xs) = x `op` (foldr op base xs)
len [] = 0
len (x:xs) = 1 + len xs
len
with foldr
!len = foldr ex2 ex3
What should ex2
and ex3
be?
What is more readable: HOF or Recursion ?
Patterns appear not just in toy functions but in real code
To practice, lets develop a small library that swizzles text files
Beginner's version, riddled with explicit recursion
Intermediate version, recursion eliminated with higher-order functions
Advanced version, swizzle
and unswizzle
without duplication