Haskell: Higher Order Functions (Part II)

Note

These slides are also available in PDF format: 4:3 PDF, 16:9 PDF, 16:10 PDF.

The $ Function

Haskell has an infix function: $. Here is how it’s defined:

($) :: (a -> b) -> a -> b
f $ x = f x

What the heck is this worthless function? It’s a function applicator: it takes a function on the left and an argument on the right, and applies the function to the argument.

So it’s still worthless, right? What if I told you that it has the lowest precedence and is right-associative?

Using $ to reduce parentheses

Function application using spaces is left-associative and high precedence, so f a b c is equivalent to (((f a) b) c).

What if a and b were functions and we wanted f (a (b c)) instead? We had to add lots of parentheses and it gets messy fast.

Let’s use $ to fix this:

-- The following two expressions are equivalent
    f (a (b c))
    f $ a $ b c

Reducing Parentheses: More Examples

  • length (filter odd [1..10])
  • length $ filter odd [1..10]
  • sum (map sqrt (filter even [1..100]))
  • sum $ map sqrt $ filter even [1..100]

More examples:

  • What does sqrt 3 + 4 + 5 compute?
  • What does sqrt $ 3 + 4 + 5 compute?

More $ Tricks: Partial Application

-- This function takes a list of functions and applies
-- [1..10] to each
onCountToTen = map ($ [1..10])

-- For example:
-- onCountToTen [filter even, filter odd, map (*2)]
-- [[2,4,6,8,10],[1,3,5,7,9],[2,4,6,8,10,12,14,16,18,20]]

$: What I expect you to know

  • How to intepret an expression which uses $
  • How to use $ to reduce parentheses
  • How to use a partial application of $ to apply an argument to a list of functions

Understanding the definition of the $ function and it’s precedence is optional, but I think it’s helpful to figure out the above.

Function Composition

In mathematics, if we have a function \(f(x)\) and \(g(x)\), we can rewrite \(f(g(x))\) as:

\[(f \circ g)(x)\]

In Haskell, this \(\circ\) can equivalently be written as .:

sumOfSquares = sum . (^2)

Which do you choose?

-- All of these are equivalent, which would you write?
crazy x y = floor (negate (tan (sin (max x y))))
crazy x y = floor $ negate $ tan $ sin $ max x y
crazy = floor . negate . tan . sin . max

Reduction Functions

A reduction function is a function which takes a list and reduces the elements in the list to a single value. For example, sum and product are reduction functions:

GHCi> sum [1..10]
55
GHCi> product [1..10]
3628800

What if we had a generalized reduction function which took a function and applied it across a sequence to obtain a result? Something like this:

reduce(f, seq) = f(f(f(seq[0], seq[1]), seq[2]), ...)

Fold Right

Haskell has a function called foldr, which takes a function, an initial value, and a list, and applies the function to each element in the list, recursively calling fold for the right value.

foldr f z []     = z
foldr f z (x:xs) = f x (foldr f z xs)

Fold Left

Haskell has a function called foldl, which takes a function, an initial value, and a list. It recurses immediately, making the new initial value the result of calling the function on the initial value and the current element.

foldl f z []     = z
foldl f z (x:xs) = foldl f (f z x) xs

Examples: Folding

-- sum using foldl
sum' = foldl (+) 0

-- sum using foldr
sum' = foldr (+) 0

-- product
product' = foldl (*) 1

Quiz Prep Time

With your new learning groups, take some time preparing for the quiz using whatever study mechanism you wish.

Topics covered:

  • Pattern Matching and Recursion
  • Let, Where, Case, Guards