Functions

Working with functions

Introduction

We often observe patterns that repeat in multiple situations and we would like to abstract out the common parts for code reuse and for concise code.

Example

1
2
3
4
5
6
7
sum :: [Int] -> Int
sum [] = 0
sum (x:xs) = x + sum xs

product :: [Int] -> Int
product [] = 1
product (x:xs) = x * product xs

Note the similarities in the two examples. Both functions combine the elements of the list with a binary operation. We can abstract out the similarities into a higher-order function.

Abstraction

We abstract out the binary operation as well as the default value into a function called aggregate, shown below:


1
2
3
4
aggregate :: (Int -> Int -> Int) -> Int ->  [Int] -> Int
aggregate combine default [] = default
aggregate combine default (x:xs) =
  combine x (aggregate combine default xs)

Now we can redefine sum and product as:


1
2
sum = aggregate (+) 0
product = aggregate (*) 1

Higher order functions allow us to abstract out common patterns and allow us to define powerful, re-usable functions.

Examples

The aggregate function is still highly specialized. The Eta standard library provides many general, useful, higher-order functions:


Map

map :: (a -> b) -> [a] -> [b]

map (* 2) [1..5] == [2,4,6,8,10]

map transforms a list by taking a function that converts each element. Note that map preserves the original structure (doesn't delete or add elements), and each element is operated on independently.


Filter

filter :: (a -> Bool) -> [a] -> [a]

filter (> 3) [1..5] == [4,5]

filter takes a list and generates a new list keeping the elements that satisfy the predicate that is supplied.

Next Section

We will explore learn about Eta metaprogramming.