# 6Functional programming

Now that we are familiar with Git and Github, we can start with writing code. We will learn how to write code using the functional programming paradigm. Programming paradigms are ways to structure programs (or scripts).

This chapter will teach you the fundamentals of functional programming. Functional programming might sound scary, but we will focus on only a handful of concepts that are quite accessible but still provide many benefits. Using these functional programming concepts will make your code more reliable, easier to test, document, share, and ultimately rerun.

## 6.1 Introduction

Remember that the philosophy of part one of this book is “don’t repeat yourself”. In this chapter we will see how we can reduce the amount of code as much as possible. In the previous chapter we’ve seen how Bruno was able to get rid of many lines of code (that were all the same) by writing a single function:

``````make_plot <- function(country_level_data,
commune_level_data,
commune){

filtered_data <- commune_level_data %>%
filter(locality == commune)

data_to_plot <- bind_rows(
country_level_data,
filtered_data
)

ggplot(data_to_plot) +
geom_line(aes(y = pl_m2,
x = year,
group = locality,
colour = locality))
}``````

Now we are going to go one step further and not only learn how to write good functions, but also how we can push the concept of “not repeating oneself” to the extreme by using higher-order functions and function factories.

You are very likely already familiar with at least two elements of functional programming: functions and lists. But functional programming is a complete programming paradigm, so using functional programming is more than simply using functions and lists (which you can use with other programming paradigms as well).

Functional programming is a paradigm that relies exclusively on the evaluation of functions to achieve the desired result. If you have already written your own functions in the past, what follows will not be very new. But in order to write a good functional program, the functions that you write and evaluate have to have certain properties. Before discussing these properties, let’s start with state.

### 6.1.1 The state of your program

Let’s suppose that you start a fresh R session, and immediately run this line:

``ls()``

If you did not modify any of R’s configuration files that get automatically loaded on startup, you should see the following:

``character(0)``

Let’s suppose that now you load some data:

``data(mtcars)``

and define a variable `a`:

``a <- 1``

Running `ls()` now shows the following:

``[1] "a"      "mtcars"``

You have just altered the state of your program. You can think of the state as a box that holds everything that gets defined by the user and is accessible at any time. Let’s now define a simple function that prints a sentence:

``````f <- function(name){
print(paste0(name, " likes lasagna"))
}

f("Bruno")``````

and here’s the output:

``[1] "Bruno likes lasagna"``

Let’s run `ls()` again:

``[1] "a"      "f"      "mtcars"``

Function `f()` is now listed there as well. This function has two nice properties:

• For a given input, it always returns exactly the same output. So `f("Bruno")` will always return “Bruno likes lasagna”.
• When running this function, the state of the program does not get altered in any way.

### 6.1.2 Predictable functions

Let’s now define another function called `g()`, which does not have the same properties as `f()`. First, let’s define a function which does not always return the same output given a particular input:

``````g <- function(name){
food <- sample(c("lasagna", "cassoulet", "feijoada"), 1)
print(paste0(name, " likes ", food))
}``````

For the same input, “Bruno”, this function now produces (potentially) a different output:

``````g("Bruno")
[1] "Bruno likes lasagna"``````
``````g("Bruno")

And now let’s consider function `h()` that modifies the state of the program:

``````h <- function(name){
food <- sample(c("lasagna", "cassoulet", "feijoada"), 1)

if(exists("food_list")){
food_list <<- append(food_list, food)
} else {
food_list <<- append(list(), food)
}

print(paste0(name, " likes ", food))
}``````

This function uses the `<<-` operator. This operator saves definitions that are made inside the body of functions (the body of a function are all the instructions that go between the curly braces) in the global environment. Before calling this function, run `ls()` again. You should see the same objects as before, plus the new functions we’ve defined:

``[1] "a"       "f"        "g"         "h"         "mtcars"``

Let’s now run `h()` once:

``````h("Bruno")

And now `ls()` again:

``[1] "a"       "f"       "food_list" "g"       "h"       "mtcars"``

Running `h()` did two things: it printed the message, but also created a variable called “food_list” in the global environment with the following contents:

``food_list``
``````[[1]]

Let’s run `h()` again:

``````h("Bruno")
[1] "Bruno likes cassoulet"``````

and let’s check the contents of “food_list”:

``food_list``
``````[[1]]

[[2]]
[1] "cassoulet"``````

If you keep running `h()`, this list will continue growing. Let me say that I hesitated to show you this; this is because if you didn’t know `<<-`, you might find the example above useful. But while useful, it is quite dangerous as well. Generally, we want to avoid using functions that change the state as much as possible because these functions are unpredictable, especially if randomness is involved. It is much safer to define `h()` like this instead:

``````h <- function(name, food_list = list()){

food <- sample(c("lasagna", "cassoulet", "feijoada"), 1)

food_list <- append(food_list, food)

print(paste0(name, " likes ", food))

food_list
}``````

The difference now is that we made `food_list` the second argument of the function. Also, we defined it as being optional by writing:

``food_list = list()``

This means that if we omit this argument, the empty list will get used by default. This avoids the users from having to manually specify it.

We can call it like this:

``````food_list <- h("Bruno", food_list)
# since food_list is
``[1] "Bruno likes feijoada"``

We save the output back to `food_list`. Let’s now check its contents:

``food_list``
``````[[1]]

[[2]]
[1] "cassoulet"

[[3]]

The only thing that we now still need to deal with is the fact that the food item gets chosen randomly. I’m going to show you the simple way of dealing with this, but later in this chapter we are going to use the `{withr}` package for situations like this. Let’s redefine `h()` one last time:

``````h <- function(name, food_list = list(), seed = 123){

# We set the seed, making sure that we get
# the same selection of food for a given seed

set.seed(seed)
food <- sample(c("lasagna", "cassoulet", "feijoada"), 1)

# We now need to unset the seed, because
# if we don't, guess what, the seed will
# stay set for the whole session!

set.seed(NULL)

food_list <- append(food_list, food)

print(paste0(name, " likes ", food))

food_list
}``````

Let’s now call `h()` several times with its default arguments:

``h("Bruno")``
``````[1] "Bruno likes feijoada"
[[1]]
``h("Bruno")``
``````[1] "Bruno likes feijoada"
[[1]]
``h("Bruno")``
``````[1] "Bruno likes feijoada"
[[1]]

As you can see, every time this function runs, it now outputs the same result. Users can change the seed to have this function output, consistently, another result.

### 6.1.3 Referentially transparent and pure functions

A referentially transparent function is a function that does not use any variable that is not also one of its inputs. For example, the following function:

``````bad <- function(x){
x + y
}``````

is not referentially transparent, because `y` is not one of the function’s inputs. What happens if you run `bad()` is that `bad()` needs to look for `y`. Because `y` is not one of its inputs, `bad()` then looks for it in the global environment. If `y` is defined there, it then gets used. Defining and using such functions must be avoided at all costs because these functions are unpredictable. For example:

``````y <- 10

x + y
}

This will return `15`. But if `y <- 45` then `bad(5)` would this time around return `50`. It is much safer, and clearer to make `y` an explicit input of the function instead of having to keep track of `y`’s value (and it’s so easy to do, why just not do it):

``````good <- function(x, y){
x + y
}``````

`good()` is a referentially transparent function; it is much safer than `bad()`. `good()` is also a pure function, because it’s a function that does not interact in any way with the global environment. It does not write anything to the global environment, nor requires anything from the global environment. Function `h()` from the previous section was not pure, because it created an object and wrote it to the global environment (the `food_list` object). Turns out that pure functions are thus necessarily referentially transparent.

So the first lesson in your functional programming journey that you have to remember is to only use pure functions.

## 6.2 Writing good functions

### 6.2.1 Functions are first-class objects

In a functional programming language, functions are first-class objects. Contrary to what the name implies, this means that functions, especially the ones you define yourself, are nothing special. A function is an object like any other, and can thus be manipulated as such. Think of anything that you can do with any object in R, and you can do the same thing with a function. For example, let’s consider the `+()` function. It takes two numeric objects and returns their sum:

``1 + 5.3``
``[1] 6.3``
``# or alternatively: `+`(1, 5.3)``

You can replace the numbers with functions that return numbers:

``sqrt(1) + log(5.3)``
``[1] 2.667707``

It’s also possible to define a function that explicitly takes another function as an input:

``````h <- function(number, f){
f(number)
}``````

You can call then use `h()` as a wrapper for `f()`:

``h(4, sqrt)``
``[1] 2``
``h(10, log10)``
``[1] 1``

Because `h()` takes another function as an argument, `h()` is called a higher-order function.

If you don’t know how many arguments `f()`, the function you’re wrapping, has, you can use the `...`:

``````h <- function(number, f, ...){
f(number, ...)
}``````

`...` are simply a place-holder for any potential additional argument that `f()` might have:

``h(c(1, 2, NA, 3), mean, na.rm = TRUE)``
``[1] 2``
``h(c(1, 2, NA, 3), mean, na.rm = FALSE)``
``[1] NA``

`na.rm` is an argument of `mean()`. As the developer of `h()`, I don’t necessarily know what `f()` might be, but even if I knew what `f()` would be and knew all its arguments, I might not want to list them all. So I can use `...` instead. The following is also possible:

``````w <- function(...){
paste0("First argument: ", ..1,
", second argument: ", ..2,
", last argument: ", ..3)
}

w(1, 2, 3)``````
``[1] "First argument: 1, second argument: 2, last argument: 3"``

If you want to learn more about `...`, type `?dots` in an R console.

Because functions are nothing special, you can also write functions that return functions. As an illustration, we’ll be writing a function that converts warnings to errors. This can be quite useful if you want your functions to fail early, which often makes debugging easier. For example, try running this:

``sqrt(-5)``
``Warning in sqrt(-5): NaNs produced``
``[1] NaN``

This only raises a warning and returns `NaN` (Not a Number). This can be quite dangerous, especially when working non-interactively, which is what we will be doing a lot later on. It is much better if a pipeline fails early due to an error, than dragging a `NaN` value. This also happens with `log10()`:

``log10(-10)``
``Warning: NaNs produced``
``[1] NaN``

So it could be useful to redefine these functions to raise an error instead, for example like this:

``````strict_sqrt <- function(x){

if(x < 0) stop("x is negative")

sqrt(x)

}``````

This function now throws an error for negative `x`:

``strict_sqrt(-10)``
``Error in strict_sqrt(-10) : x is negative``

However, it can be quite tedious to redefine every function that we need in our pipeline, and remember, we don’t want to repeat ourselves. So, because functions are nothing special, we can define a function that takes a function as an argument, converts any warning thrown by that function into an error, and returns a new function. For example:

``````strictly <- function(f){
function(...){
tryCatch({
f(...)
},
warning = function(warning)stop("Can't do that chief"))
}
}``````

This function makes use of `tryCatch()` which catches warnings raised by an expression (in this example the expression is `f(...)`) and then raises an error instead with the `stop()` function. It is now possible to define new functions like this:

``s_sqrt <- strictly(sqrt)``
``s_sqrt(-4)``
``Error in value[[3L]](cond) : Can't do that chief``
``s_log <- strictly(log)``
``s_log(-4)``
``Error in value[[3L]](cond) : Can't do that chief``

Functions that return functions are called function factories and they’re incredibly useful. I use this so much that I’ve written a package, available on CRAN, called `{chronicler}`, that does this:

``s_sqrt <- chronicler::record(sqrt)``
``````result <- s_sqrt(-4)

result``````
``````NOK! Value computed unsuccessfully:
---------------
Nothing

---------------
This is an object of type `chronicle`.
Retrieve the value of this object with pick(.c, "value").

Because the expression above resulted in an error, `Nothing` is returned. `Nothing` is a special value defined in the `{maybe}` package (check it out, a very interesting package!). We can then even read a log to see what went wrong:

``chronicler::read_log(result)``
``````[1] "Complete log:"
[2] "NOK! sqrt() ran unsuccessfully with following exception: NaNs produced at 2024-07-14 15:23:16"
[3] "Total running time: 0.000793218612670898 secs"                                                ``````

The `{purrr}` package also comes with function factories that you might find useful (`{possibly}`, `{safely}` and `{quietly}`).

In part 2 we will also learn about assertive programming, another way of making our functions safer, as an alternative to using function factories.

### 6.2.2 Optional arguments

It is possible to make functions’ arguments optional, by using `NULL`. For example:

``````g <- function(x, y = NULL){
if(is.null(y)){
print("optional argument y is NULL")
x
} else {
if(y == 5) print("y is present"); x+y
}
}``````

Calling `g(10)` prints the message “Optional argument y is NULL”, and returns 10. Calling `g(10, 5)` however, prints “y is present” and returns 15. It is also possible to use `missing()`:

``````g <- function(x, y){
if(missing(y)){
print("optional argument y is missing")
x
} else {
if(y == 5) print("y is present"); x+y
}
}``````

I however prefer the first approach, because it is clearer which arguments are optional, which is not the case with the second approach, where you need to read the body of the function.

### 6.2.3 Safe functions

It is important that your functions are safe and predictable. You should avoid writing functions that behave like the `nchar()` base function. Let’s see why this function is not safe:

``nchar("10000000")``
``[1] 8``

It returns the expected result of 8. But what if I remove the quotes?

``nchar(10000000)``
``[1] 5``

What is going on here? I’ll give you a hint: simply type `10000000` in the console:

``10000000``
``[1] 1e+07``

`10000000` gets represented as `1e+07` by R. This number in scientific notation gets then converted into the character “1e+07” by `nchar()`, and this conversion happens silently. `nchar()` then counts the number of characters, and correctly returns 5. The problem is that it doesn’t make sense to provide a number to a function that expects a character. This function should have returned an error message, or at the very least raised a warning that the number got converted into a character. Here is how you could rewrite `nchar()` to make it safer:

``````nchar2 <- function(x, result = 0){

if(!isTRUE(is.character(x))){
stop(paste0("x should be of type 'character', but is of type '",
} else if(x == ""){
result
} else {
result <- result + 1
split_x <- strsplit(x, split = "")[[1]]
nchar2(paste0(split_x[-1],
collapse = ""), result)
}
}``````

This function now returns an error message if the input is not a character:

``nchar2(10000000)``
``Error in nchar2(10000000) : x should be of type 'character', but is of type 'integer' instead.``

This section is in a sense an introduction to assertive programming. As mentioned in the section on function factories, we will be learning about assertive programming in greater detail in part 2 of the book.

### 6.2.4 Recursive functions

You may have noticed in the last lines of `nchar2()` (defined above) that `nchar2()` calls itself. A function that calls itself in its own body is called a recursive function. It is sometimes easier to define a function in its recursive form than in an iterative form. The most common example is the factorial function. However, there is an issue with recursive functions (in the R programming language, other programming languages may not have the same problem, like Haskell): while it is sometimes easier to write a function using a recursive algorithm than an iterative algorithm, like for the factorial function, recursive functions in R are quite slow. Let’s take a look at two definitions of the factorial function, one recursive, the other iterative:

``````fact_iter <- function(n){
result = 1
for(i in 1:n){
result = result * i
}
result
}

fact_recur <- function(n){
if(n == 0 || n == 1){
result = 1
} else {
n * fact_recur(n-1)
}
}``````

Using the `{microbenchmark}` package we can benchmark the code:

``````microbenchmark::microbenchmark(
fact_recur(50),
fact_iter(50)
)``````
``````Unit: microseconds
expr    min     lq     mean median      uq    max neval
fact_recur(50) 21.501 21.701 23.82701 21.901 22.0515 68.902   100
fact_iter(50)  2.000  2.101  2.74599  2.201  2.3510 21.000   100``````

We see that the recursive factorial function is 10 times slower than the iterative version. In this particular example it doesn’t make much of a difference, because the functions only take microseconds to run. But if you’re working with more complex functions, this is a problem. If you want to keep using the recursive function and not switch to an iterative algorithm, there are ways to make them faster. The first is called trampolining. I won’t go into details, but if you’re interested, there is an R package that allows you to use trampolining with R, aptly called `{trampoline}`1. Another solution is using the `{memoise}`2 package. Again, I won’t go into details. So if you want to use and optimize recursive functions, take a look at these packages.

### 6.2.5 Anonymous functions

It is possible to define a function and not give it a name. For example:

``function(x)(x+1)(10)``

Since R version 4.1, there is even a shorthand notation for anonymous functions:

``(\(x)(x+1))(10)``

Because we don’t name them, we cannot reuse them. So why is this useful? Anonymous functions are useful when you need to apply a function somewhere inside a pipe once, and don’t want to define a function just for this. This will become clearer once we learn about lists, but before that, let’s philosophize a bit.

### 6.2.6 The Unix philosophy applied to R

This is the Unix philosophy: Write programs that do one thing and do it well. Write programs to work together. Write programs to handle text streams, because that is a universal interface.

Doug McIlroy, in A Quarter Century of Unix3

We can take inspiration from the Unix philosophy and rewrite it for our purposes:

Write functions that do one thing and do it well. Write functions that work together. Write functions that handle lists, because that is a universal interface.

Strive for writing simple functions that only perform one task. Don’t hesitate to split a big function into smaller ones. Small functions that only perform one task are easier to maintain, test, document and debug. These smaller functions can then be chained using the `|>` operator. In other words, it is preferable to have something like:

``a |> f() |> g() |> h()``

where `a` is for example a path to a data set, and where `f()`, `g()` and `h()` successively read, clean, and plot the data, than having something like:

``big_function(a)``

that does all the steps above in one go.

This idea of splitting the problem into smaller chunks, each chunk in turn split into even smaller units that can be handled by functions and then the results of these function combined into a final output is called composition.

The advantage of splitting `big_function()` into `f()`, `g()` and `h()` is that you can eat the elephant one bite at a time, and also reuse these smaller functions in other projects more easily. So what’s important is that you can make small functions work together by sharing a common interface. The list is usually a good candidate for this.

## 6.3 Lists: a powerful data-structure

Lists are the second important ingredient of functional programming. In the R philosophy inspired by the UNIX philosophy, I stated that lists are a universal interface in R, so our functions should handle lists. This of course depends on what it is you’re doing. If you need functions to handle numbers, then there’s little value in placing these numbers inside lists. But in practice, you will very likely manipulate objects that are more complex than numbers, and this is where lists come into play.

### 6.3.1 Lists all the way down

Lists are extremely flexible, and most of the very complex objects classes that you manipulate are actually lists, but just fancier. For example, a data frame is a list:

``````data(mtcars)

typeof(mtcars)``````
``[1] "list"``

A fitted model is a list:

``````my_model <- lm(hp ~ mpg, data = mtcars)

typeof(my_model)``````
``[1] "list"``

A `ggplot` is a list:

``````library(ggplot2)

my_plot <- ggplot(data = mtcars) +
geom_line(aes(y = hp, x = mpg))

typeof(my_plot)``````
``[1] "list"``

It’s lists all the way down, and it’s not a coincidence; it’s because lists are very powerful. So it’s important to know what you can do with lists.

### 6.3.2 Lists can hold many things

If you write a function that needs to return many objects, the only solution is to place them inside a list. For example, consider this function:

``````sqrt_newton <- function(a,
init = 1,
eps = 0.01,
steps = 1){

stopifnot(a >= 0)
while(abs(init**2 - a) > eps){
init <- 1/2 *(init + a/init)
steps <- steps + 1
}
list(
"result" = init,
"steps" = steps
)
}``````

This function returns the square root of a number using Newton’s algorithm, as well as the number of steps, or iterations, it took to reach the solution:

``````result_list <- sqrt_newton(1600)

result_list``````
``````\$result
[1] 40

\$steps
[1] 10``````

It is quite common to print the number of steps to the console instead of returning them. But the issue with a function that prints something to the console instead of returning it, is that such a function is not pure, as it changes something outside of its scope (it prints to the console!). And if you need the information that got printed (for example, if you want to count all the steps it took to run the script from start to finish), it is lost. It gets printed, and that’s it. It is preferable to instead make the function pure by returning everything inside a neat list. It is then possible to separately save these objects if needed:

``````result <- result_list\$result

result_steps <- result_list\$steps``````

Or you could define functions that know how to deal with the list:

``````f <- function(result_list){
list(
"result" = result_list\$result * 10,
"steps" = result_list\$steps + 1
)
}

f(result_list)``````
``````\$result
[1] 400

\$steps
[1] 11``````

It all depends on what you want to do. But it is usually better to keep everything neatly inside a list.

Lists can also hold objects of different types:

``````list(
"b" = ~lm(y ~ x)
)``````
``````\$a
mpg cyl disp  hp drat    wt  qsec vs am
Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1
Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1
Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1
Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0
Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0
Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0
gear carb
Mazda RX4            4    4
Mazda RX4 Wag        4    4
Datsun 710           4    1
Hornet 4 Drive       3    1
Valiant              3    1

\$b
~lm(y ~ x)``````

The list above has two elements, the first is the head of the `mtcars` data frame, the second is a formula object. Lists can even hold other lists:

``````list(
"b" = list(
"c" = sqrt,
"d" = my_plot # Remember this ggplot object from before?
)
)``````
``````\$a
mpg cyl disp  hp drat    wt  qsec vs am
Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1
Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1
Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1
Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0
Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0
Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0
gear carb
Mazda RX4            4    4
Mazda RX4 Wag        4    4
Datsun 710           4    1
Hornet 4 Drive       3    1
Valiant              3    1

\$b
\$b\$c
function (x)  .Primitive("sqrt")

\$b\$d``````

### 6.3.3 Lists as the cure to loops

Loops are incredibly useful, and you are likely familiar with them. The problem with loops is that they are a concept from iterative programming, not functional programming, and this is a problem because loops rely on changing the state of your program to run. For example, let’s suppose that you wish to use a for-loop to compute the sum of the first 100 integers:

``````result <- 0

for (i in 1:100){
result <- result + i
}

print(result)``````
``[1] 5050``

If you run `ls()` now, you should see that there’s a variable `i` in your global environment. This could cause issues further down in your pipeline if you need to re-use `i`. Also, writing loops is, in my opinion, quite error prone. But how can we avoid using loops? Looping in a functional programming language involves using higher-order functions and lists. A reminder: a higher-order function is a function that takes another function as an argument. Looping is a task like any other, so I can write a function that does the looping. This function, which I’ll call `looping()`, will take a function as an argument, as well as a list. The list will serve as the container to hold our numbers:

``````looping <- function(a_list, a_func, init = NULL, ...){

# If the user does not provide an `init` value,
# set the head of the list as the initial value
if(is.null(init)){
init <- a_list[[1]]
a_list <- tail(a_list, -1)
}

# Separate the head from the tail of the list
# and apply the function to the initial value and the head of the list
tail_list = tail(a_list, -1)

# Check if we're done: if there is still some tail,
# rerun the whole thing until there's no tail left
if(length(tail_list) != 0){
looping(tail_list, a_func, init, ...)
}
else {
init
}
}``````

Now, this might seem much more complicated than a for loop. However, now that we have abstracted the loop away inside a function, we can keep reusing this function:

``looping(as.list(seq(1, 100)), `+`)``
``[1] 5050``

Of course, because this is so useful, `looping()` actually ships with R, and is called `Reduce()`:

``Reduce(`+`, seq(1, 100)) # the order of the arguments is `function` then `list` for `Reduce()```
``[1] 5050``

But this is not the only way that we can loop. We can also write a loop that applies a function to each element of a list, instead of operating on the whole list:

``````result <- as.list(seq(1, 5))
for (i in seq_along(result)){
result[[i]] <- sqrt(result[[i]])
}

print(result)``````
``````[[1]]
[1] 1

[[2]]
[1] 1.414214

[[3]]
[1] 1.732051

[[4]]
[1] 2

[[5]]
[1] 2.236068``````

Here again, we have to pollute the global environment by first creating a vessel for our results, and then apply the function at each index. We can abstract this process away in a function:

``````applying <- function(a_list, a_func, ...){

tail_list = tail(a_list, -1)

# Check if we're done: if there is still some tail, rerun the whole thing until there's no tail left
if(length(tail_list) != 0){
append(result, applying(tail_list, a_func, ...))
}
else {
result
}
}``````

Once again this might seem complicated, and I would agree. Abstraction is complex. But once we have it, we can focus on the task at hand, instead of having to always tell the computer what we want:

``applying(as.list(seq(1, 5)), sqrt)``
``[1] 1.000000 1.414214 1.732051 2.000000 2.236068``

Of course, R ships with its own, much more efficient, implementation of this function:

``lapply(list(seq(1, 5)), sqrt)``
``````[[1]]
[1] 1.000000 1.414214 1.732051 2.000000 2.236068``````

In other programming languages, `lapply()` is often called `map()`. The `{purrr}` package ships with other such useful higher-order functions that abstract loops away. For example, there’s the function called `map2()`, that maps a function of two arguments to each element of two atomic vectors or lists, two at a time:

``````library(purrr)

map2(
.x = seq(1, 5),
.y = seq(1, 5),
.f = `+`
)``````
``````[[1]]
[1] 2

[[2]]
[1] 4

[[3]]
[1] 6

[[4]]
[1] 8

[[5]]
[1] 10``````

If you have more than two lists, you can use `pmap()` instead.

Another important, idiomatic, way to deal with loops in R is to use matrix algebra instead. For example, to compute the sum of the first 100 integers, the following approach is possible:

``rep(1, 100) %*% seq(1, 100)``
``````     [,1]
[1,] 5050``````

Also, don’t forget that many functions are vectorized by default, so no loop is required:

``sqrt(seq(1, 5))``
``[1] 1.000000 1.414214 1.732051 2.000000 2.236068``

or:

``seq(1, 5) + seq(1, 5)``
``[1]  2  4  6  8 10``

Before diving directly into loops, check if the functions you’re using are vectorized, or if there is a simple way to express the computation you want to run in terms of matrix multiplication.

### 6.3.4 Data frames

As mentioned in the introduction of this section, data frames are a special type of list of atomic vectors. This means that just as I can use `lapply()` to compute the square root of the elements of an atomic vector, as shown previously, I can also operate on all the columns of a data frame. For example, it is possible to determine the class of every column of a data frame like this:

``lapply(iris, class)``
``````\$Sepal.Length
[1] "numeric"

\$Sepal.Width
[1] "numeric"

\$Petal.Length
[1] "numeric"

\$Petal.Width
[1] "numeric"

\$Species
[1] "factor"``````

Unlike a list however, the elements of a data frame must be of the same length. Data frames remain very flexible though, and using what we have learned until now it is possible to use the data frame as a structure for all our computations. For example, suppose that we have a data frame that contains data on unemployment for the different subnational divisions of the Grand-Duchy of Luxembourg, the country the author of this book hails from. Let’s suppose that I want to generate several plots, per subnational division and per year. Typically, we would use a loop for this, but we can use what we’ve learned here, as well as some functions from the `{dplyr}`, `{purrr}`, `{ggplot2}` and `{tidyr}` packages. I will be downloading data that I made available inside a package, but instead of installing the package, I will download the `.rda` file directly (which is the file format of packaged data) and then load that data into our R session (instead of downloading from the long Github url, I download the data from a shortened is.gd link):

``````# Create a temporary file
unemp_path <- tempfile(fileext = ".rda")

# Download the data and save it to the path of the temporary file
# avoids having to install the package from Github
"https://is.gd/l57cNX",
destfile = unemp_path)

# Load the data. The data is now available as 'unemp'

Let’s load the required packages and take a look at the data:

``library(dplyr)``
``````
Attaching package: 'dplyr'``````
``````The following objects are masked from 'package:stats':

filter, lag``````
``````The following objects are masked from 'package:base':

intersect, setdiff, setequal, union``````
``````library(purrr)
library(ggplot2)
library(tidyr)

glimpse(unemp)``````
``````Rows: 472
Columns: 9
\$ year                         <dbl> 2013, 2013, 2013, 201…
\$ place_name                   <chr> "Luxembourg", "Capell…
\$ level                        <chr> "Country", "Canton", …
\$ total_employed_population    <dbl> 223407, 17802, 1703, …
\$ of_which_wage_earners        <dbl> 203535, 15993, 1535, …
\$ of_which_non_wage_earners    <dbl> 19872, 1809, 168, 94,…
\$ unemployed                   <dbl> 19287, 1071, 114, 25,…
\$ active_population            <dbl> 242694, 18873, 1817, …
\$ unemployment_rate_in_percent <dbl> 7.947044, 5.674773, 6…``````

Column names are self-descriptive, but the `level` column needs some explanations. `level` contains the administrative divisions of the country, so the country of Luxembourg, then the Cantons and then the Communes.

Remember that Luxembourg can refer to the country, the canton or the commune of Luxembourg. Now let’s suppose that I want a separate plot for the three communes of Luxembourg, Esch-sur-Alzette and Wiltz. Instead of creating three separate data frames and feeding them to the same ggplot code, I can instead take advantage of the fact that data frames are lists, and are thus quite flexible. Let’s start with filtering:

``````filtered_unemp <- unemp %>%
filter(
level == "Commune",
place_name %in% c("Luxembourg", "Esch-sur-Alzette", "Wiltz")
)

glimpse(filtered_unemp)``````
``````Rows: 12
Columns: 9
\$ year                         <dbl> 2013, 2013, 2013, 201…
\$ place_name                   <chr> "Esch-sur-Alzette", "…
\$ level                        <chr> "Commune", "Commune",…
\$ total_employed_population    <dbl> 12725, 39513, 2344, 1…
\$ of_which_wage_earners        <dbl> 12031, 35531, 2149, 1…
\$ of_which_non_wage_earners    <dbl> 694, 3982, 195, 703, …
\$ unemployed                   <dbl> 2054, 3855, 318, 1997…
\$ active_population            <dbl> 14779, 43368, 2662, 1…
\$ unemployment_rate_in_percent <dbl> 13.898099, 8.889043, …``````

We are now going to use the fact that data frames are lists, and that lists can hold any type of object. For example, remember this list from before where one of the elements is a data frame, and the second one a formula:

``````list(
"b" = ~lm(y ~ x)
)``````
``````\$a
mpg cyl disp  hp drat    wt  qsec vs am
Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1
Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1
Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1
Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0
Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0
Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0
gear carb
Mazda RX4            4    4
Mazda RX4 Wag        4    4
Datsun 710           4    1
Hornet 4 Drive       3    1
Valiant              3    1

\$b
~lm(y ~ x)``````

`{dplyr}` comes with a function called `group_nest()` which groups the data frame by a variable (such that the next computations will be performed group-wise) and then nests the other columns into a smaller data frame. Let’s try it and see what happens:

``````nested_unemp <- filtered_unemp %>%
group_nest(place_name)``````

Let’s see what this looks like:

``nested_unemp``
``````# A tibble: 3 × 2
place_name                     data
<chr>            <list<tibble[,8]>>
1 Esch-sur-Alzette            [4 × 8]
2 Luxembourg                  [4 × 8]
3 Wiltz                       [4 × 8]``````

`nested_unemp` is a new data frame of 3 rows, one per commune (“Esch-sur-Alzette”, “Luxembourg”, “Wiltz”), and of two columns, one for the names of the communes, and the other contains every other variable inside a smaller data frame. So this is a data frame that has one column where each element of that column is itself a data frame. Such a column is called a list-column. This is essentially a list of lists.

Let’s now think about this for a moment. If the column titled `data` is a list of data frames, it should be possible to use a function like `map()` or `lapply()` to apply a function on each of these data frames. Remember that `map()` or `lapply()` require a list of elements of whatever type and a function that accepts objects of this type as input. So this means that we could apply a function that plots the data to each element of the column titled `data`. Since each element of this column is a data frame, this function needs a data frame as an input. As a first and simple example to illustrate this, let’s suppose that we want to determine the number of rows of each data frame. This is how we would do it:

``````nested_unemp %>%
mutate(nrows = map(data, nrow))``````
``````# A tibble: 3 × 3
place_name                     data nrows
<chr>            <list<tibble[,8]>> <list>
1 Esch-sur-Alzette            [4 × 8] <int [1]>
2 Luxembourg                  [4 × 8] <int [1]>
3 Wiltz                       [4 × 8] <int [1]>``````
`````` # ’data’ is the name of
# the list-column that contains
# the smaller data frames``````

The new column, titled `nrows` is a list of integers. We can simplify it by converting it directly to an atomic vector of integers by using `map_int()` instead of `map()`:

``````nested_unemp %>%
mutate(nrows = map_int(data, nrow))``````
``````# A tibble: 3 × 3
place_name                     data nrows
<chr>            <list<tibble[,8]>> <int>
1 Esch-sur-Alzette            [4 × 8]     4
2 Luxembourg                  [4 × 8]     4
3 Wiltz                       [4 × 8]     4``````

Let’s try a more complex example now. What if we want to filter rows (of course, the simplest way would be to filter the rows we need before nesting the data frame)? We need to apply the function `filter()` where its first argument is a data frame and the second argument is a predicate:

``````nested_unemp %>%
mutate(nrows = map(data, \(x)filter(x, year == 2015)))``````
``````# A tibble: 3 × 3
place_name                     data nrows
<chr>            <list<tibble[,8]>> <list>
1 Esch-sur-Alzette            [4 × 8] <tibble [1 × 8]>
2 Luxembourg                  [4 × 8] <tibble [1 × 8]>
3 Wiltz                       [4 × 8] <tibble [1 × 8]>``````

In this case, we need to use an anonymous function. This is because `filter()` has two arguments and we need to make clear what it is we are mapping over and what argument stays fixed; we are mapping over (iterating) the data frames but the predicate `year == 2015` stays fixed.

We are now ready to plot our data. The best way to continue is to first get the function right by creating one plot for one single commune. Let’s select the dataset for the commune of `Luxembourg`:

``````lux_data <- nested_unemp %>%
filter(place_name == "Luxembourg") %>%
unnest(data)``````

To plot this data, we can now write the required `ggplot2()` code:

``````ggplot(data = lux_data) +
theme_minimal() +
geom_line(
aes(year, unemployment_rate_in_percent, group = 1)
) +
labs(title = "Unemployment in Luxembourg")``````

To turn the lines of code above into a function, you need to think about how many arguments that function would have. There is an obvious one, the data itself (in the snippet above, the data is the `lux_data` object). Another one that is less obvious is in the title:

``labs(title = "Unemployment in Luxembourg")``

Ideally, we would want that title to change depending on the data set. So we could write the function like so:

``````make_plot <- function(x, y){
ggplot(data = x) +
theme_minimal() +
geom_line(
aes(year, unemployment_rate_in_percent, group = 1)
) +
labs(title = paste("Unemployment in", y))
}``````

Let’s try it on our data:

``make_plot(lux_data, "Luxembourg")``

Ok, so now, we simply need to apply this function to our nested data frame:

``````nested_unemp <- nested_unemp %>%
mutate(plots = map2(
.x = data, # column of data frames
.y = place_name, # column of commune names
.f = make_plot
))

nested_unemp``````
``````# A tibble: 3 × 3
place_name                     data plots
<chr>            <list<tibble[,8]>> <list>
1 Esch-sur-Alzette            [4 × 8] <gg>
2 Luxembourg                  [4 × 8] <gg>
3 Wiltz                       [4 × 8] <gg>  ``````

If you look at the `plots` column, you see that it is a list of `gg` objects: these are our plots. Let’s take a look at them:

``nested_unemp\$plots``
``[[1]]``

``````
[[2]]``````

``````
[[3]]``````

We could also have used an anonymous function (but it is more difficult to get right):

``````nested_unemp %>%
mutate(plots2 = map2(
.x = data,
.y = place_name,
.f = \(.x,.y)(
ggplot(data = .x) +
theme_minimal() +
geom_line(
aes(year, unemployment_rate_in_percent, group = 1)
) +
labs(title = paste("Unemployment in", .y))
)
)
) %>%
pull(plots2)``````
``[[1]]``

``````
[[2]]``````

``````
[[3]]``````

This list-column based workflow is extremely powerful and I highly advise you to take the required time to master it. Remember, we never want to have to repeat ourselves. This approach might seem more complicated than calling `make_plot()` three times, but imagine that you need to do this for several countries, several variables, etc… What are you going to do, copy and paste code everywhere? This gets very tedious and more importantly, very error-prone, because now you’ve just introduced many points of failure by having so much copy-pasted code. You could of course use a loop instead of this list-column based workflow. But as mentioned, the issue with loops is that you have to interact with the global environment, which can lead to other issues. But whatever you end up using, you need to avoid copy and pasting at all costs.

## 6.4 Functional programming in R

Up until now I focused on general concepts rather than on specifics of the R programming language when it comes to functional programming. In this section, we will be focusing entirely on R-specific capabilities and packages for functional programming.

### 6.4.1 Base capabilities

R is a functional programming language (but not only), and as such it comes with many functions out of the box to write functional code. We have already discussed `lapply()` and `Reduce()`. You should know that depending on what you want to achieve, there are other functions that are similar to `lapply()`: `apply()`, `sapply()`, `vapply()`, `mapply()` and `tapply()`. There’s also `Map()` which is a wrapper around `mapply()`. Each function performs the same basic task of applying a function over all the elements of a list or list-like structure, but it can be hard to keep them apart and when you should use one over another. This is why `{purrr}`, which we will discuss in the next section, is quite an interesting alternative to base R’s offering.

Another one of the quintessential functional programming functions (alongside `Reduce()` and `Map()`) that ships with R is `Filter()`. If you know `dplyr::filter()` you should be familiar with the concept of filtering rows of a data frame where the elements of one particular column satisfy a predicate. `Filter()` works the same way, but focusing on lists instead of data frame:

``````Filter(is.character,
list(
seq(1, 5),
"Hey")
)``````
``````[[1]]
[1] "Hey"``````

The call above only returns the elements where `is.character()` evaluates to `TRUE`.

Another useful function is `Negate()` which is a function factory that takes a boolean function as an input and returns the opposite boolean function. As an illustration, suppose that in the example above we wanted to get everything but the character:

``````Filter(Negate(is.character),
list(
seq(1, 5),
"Hey")
)``````
``````[[1]]
[1] 1 2 3 4 5``````

There are some other functions like this that you might want to check out: type `?Negate` in console to read more about them.

Sometimes you may need to run code with side-effects, but want to avoid any interaction between these side-effects and the global environment. For example, you might want to run some code that creates a plot and saves it to disk, or code that creates some data and writes them to disk. `local()` can be used for this. `local()` runs code in a temporary environment that gets discarded at the end:

``````local({
a <- 2
})``````

Variable `a` was created inside this local environment. Checking if it exists now yields `FALSE`:

``exists("a")``
``[1] FALSE``

We will be using this technique later in the book to keep our scripts pure.

Before continuing with R packages that extend R’s functional programming capabilities it’s also important to stress that just as R is a functional programming language, it is also an object oriented language. In fact, R is what John Chambers called a functional OOP language (Chambers (2014)). I won’t delve too much into what this means (read Wickham (2019) for this), but as a short discussion, consider the `print()` function. Depending on what type of object the user gives it, it seems as if somehow `print()` knows what to do with it:

``print(5)``
``[1] 5``
``print(head(mtcars))``
``````                   mpg cyl disp  hp drat    wt  qsec vs am
Mazda RX4         21.0   6  160 110 3.90 2.620 16.46  0  1
Mazda RX4 Wag     21.0   6  160 110 3.90 2.875 17.02  0  1
Datsun 710        22.8   4  108  93 3.85 2.320 18.61  1  1
Hornet 4 Drive    21.4   6  258 110 3.08 3.215 19.44  1  0
Hornet Sportabout 18.7   8  360 175 3.15 3.440 17.02  0  0
Valiant           18.1   6  225 105 2.76 3.460 20.22  1  0
gear carb
Mazda RX4            4    4
Mazda RX4 Wag        4    4
Datsun 710           4    1
Hornet 4 Drive       3    1
Valiant              3    1``````
``print(str(mtcars))``
``````'data.frame':   32 obs. of  11 variables:
\$ mpg : num  21 21 22.8 21.4 18.7 18.1 14.3 24.4 22.8 19.2 ...
\$ cyl : num  6 6 4 6 8 6 8 4 4 6 ...
\$ disp: num  160 160 108 258 360 ...
\$ hp  : num  110 110 93 110 175 105 245 62 95 123 ...
\$ drat: num  3.9 3.9 3.85 3.08 3.15 2.76 3.21 3.69 3.92 3.92 ...
\$ wt  : num  2.62 2.88 2.32 3.21 3.44 ...
\$ qsec: num  16.5 17 18.6 19.4 17 ...
\$ vs  : num  0 0 1 1 0 1 0 1 1 1 ...
\$ am  : num  1 1 1 0 0 0 0 0 0 0 ...
\$ gear: num  4 4 4 3 3 3 3 4 4 4 ...
\$ carb: num  4 4 1 1 2 1 4 2 2 4 ...
NULL``````

This works by essentially mixing both functional and object-oriented programming, hence functional OOP. Let’s take a closer look at the source code of `print()` by simply typing `print` without brackets, into a console:

``print``
``````function (x, ...)
UseMethod("print")
<bytecode: 0x558c98972ff0>
<environment: namespace:base>``````

Quite unexpectedly, the source code of `print()` is one line long and is just `UseMethod("print")`. So all `print()` does is use a generic method called “print”. If your text editor has auto-completion enabled, you might see that there are actually many `print()` functions. For example, type `print.data.frame` into a console:

``print.data.frame``
``````function (x, ..., digits = NULL, quote = FALSE, right = TRUE,
row.names = TRUE, max = NULL)
{
n <- length(row.names(x))
if (length(x) == 0L) {
cat(sprintf(ngettext(n, "data frame with 0 columns and %d row",
"data frame with 0 columns and %d rows"), n), "\n",
sep = "")
}
else if (n == 0L) {
print.default(names(x), quote = FALSE)
cat(gettext("<0 rows> (or 0-length row.names)\n"))
}
else {
if (is.null(max))
max <- getOption("max.print", 99999L)
if (!is.finite(max))
stop("invalid 'max' / getOption(\"max.print\"): ",
max)
omit <- (n0 <- max%/%length(x)) < n
m <- as.matrix(format.data.frame(if (omit)
x[seq_len(n0), , drop = FALSE]
else x, digits = digits, na.encode = FALSE))
if (!isTRUE(row.names))
dimnames(m)[[1L]] <- if (isFALSE(row.names))
rep.int("", if (omit)
n0
else n)
else row.names
print(m, ..., quote = quote, right = right, max = max)
if (omit)
cat(" [ reached 'max' / getOption(\"max.print\") -- omitted",
n - n0, "rows ]\n")
}
invisible(x)
}
<bytecode: 0x558c9a2a5728>
<environment: namespace:base>``````

This is the `print` function for `data.frame` objects. So what `print()` does, is look at the class of its argument `x`, and then look for the right `print` function to call. In more traditional OOP languages, users would type something like:

``mtcars.print()``

In these languages, objects encapsulate methods (the equivalent of our functions), so if `mtcars` is a data frame, it encapsulates a `print()` method that then does the printing. R is different, because classes and methods are kept separate. If a package developer creates a new object class, then the developer also must implement the required methods. For example in the `{chronicler}` package, the `chronicler` class is defined alongside the `print.chronicler()` function to print these objects.

All of this to say that if you want to extend R by writing packages, learning some OOP essentials is also important. But for data analysis, functional programming does the job perfectly well. To learn more about R’s different OOP systems (yes, R can do OOP in different ways and the one I sketched here is the simplest, but probably the most used as well), take a look at Wickham (2019).

### 6.4.2 purrr

The `{purrr}` package, developed by Posit (formerly RStudio), contains many functions to make functional programming with R more smooth. In the previous section, we discussed the `apply()` family of function; they all do a very similar thing, which is looping over a list and applying a function to the elements of the list, but it is not quite easy to remember which one does what. Also, for some of these functions like `apply()`, the list argument comes first, and then the function, but in the case of `mapply()`, the function comes first. This type of inconsistencies can be frustrating. Another issue with these functions is that it is not always easy to know what type the output is going to be. List? Atomic vector? Something else?

`{purrr}` solves this issue by offering the `map()` family of functions, which behave in a very consistent way. The basic function is called `map()` and we’ve already used it:

``map(seq(1, 5), sqrt)``
``````[[1]]
[1] 1

[[2]]
[1] 1.414214

[[3]]
[1] 1.732051

[[4]]
[1] 2

[[5]]
[1] 2.236068``````

But there are many interesting variants:

``map_dbl(seq(1, 5), sqrt)``
``[1] 1.000000 1.414214 1.732051 2.000000 2.236068``

`map_dbl()` coerces the output to an atomic vector of doubles instead of a list of doubles. Then there’s:

``map_chr(letters, toupper)``
`````` [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N"
[15] "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"``````

for when the output needs to be an atomic vector of characters.

There are many others, so take a look at the documentation with `?map`. There’s also `walk()` which is used if you’re only interested in the side-effect of the function (for example if the function takes paths as input and saves something to disk).

`{purrr}` also has functions to replace `Reduce()`, simply called `reduce()` and `accumulate()`, and there are many, many other useful functions. Read through the documentation of the package4 and take the time to learn about all it has to offer.

### 6.4.3 withr

`{withr}` is a powerful package that makes it easy to “purify” functions that behave in a way that can cause problems. Remember the function from the introduction that randomly gave out a dish Bruno liked? Here it is again:

``````h <- function(name, food_list = list()){

food <- sample(c("lasagna", "cassoulet", "feijoada"), 1)

food_list <- append(food_list, food)

print(paste0(name, " likes ", food))

food_list
}``````

For the same input, this function may return different outputs so this function is not referentially transparent. So we improved the function by adding calls to `set.seed()` like this:

``````h2 <- function(name, food_list = list(), seed = 123){

# We set the seed, making sure that we get the same selection of food for a given seed
set.seed(seed)
food <- sample(c("lasagna", "cassoulet", "feijoada"), 1)

# We now need to unset the seed, because if we don't, guess what, the seed will stay set for the whole session!
set.seed(NULL)

food_list <- append(food_list, food)

print(paste0(name, " likes ", food))

food_list
}``````

The problem with this approach is that we need to modify our function. We can instead use `withr::with_seed()` to achieve the same effect:

``````withr::with_seed(seed = 123,
h("Bruno"))``````
``[1] "Bruno likes feijoada"``
``````[[1]]

It is also easier to create a wrapper if needed:

``````h3 <- function(..., seed){
withr::with_seed(seed = seed,
h(...))
}``````
``h3("Bruno", seed = 123)``
``[1] "Bruno likes feijoada"``
``````[[1]]

In a previous example we downloaded a dataset and loaded it into memory; we did so by first creating a temporary file, then downloading it and then loading it. Suppose that instead of loading this data into our session, we simply wanted to test whether the link was still working. We wouldn’t want to keep the loaded data in our session, so to avoid having to delete it again manually, we could use `with_tempfile()`:

``````withr::with_tempfile("unemp", {
"https://is.gd/l57cNX",
destfile = unemp)
nrow(unemp)
}
)``````
``[1] 472``

The data got downloaded, and then loaded, and then we computed the number of rows of the data, without touching the global environment, or state, of our current session.

Just like for `{purrr}`, `{withr}` has many useful functions which I encourage you to familiarize yourself with5.

## 6.5 Conclusion

If there is only one thing that you should remember from this chapter, it would be pure functions. Writing pure functions is in my opinion not very difficult to do and comes with many benefits. But avoiding loops and replacing them with higher-order functions (`lapply()`, `Reduce()`, `purrr::map()` – and its variants –) also pays off. While this chapter stresses the advantages of functional programming, you should not forget that R is not a pure, and solely, functional programming language and that other paradigms, like object-oriented programming, are also available to you. So if your goal is to master the language (instead of “just” using it to solve data analysis problems), then you also need to know about R’s OOP capabilities.

1. https://rdinnager.github.io/trampoline/↩︎

2. https://memoise.r-lib.org/↩︎

3. http://www.catb.org/~esr/writings/taoup/html/ch01s06.html↩︎

4. https://purrr.tidyverse.org/reference/index.html↩︎

5. https://withr.r-lib.org/reference/index.html↩︎