1. Tools for functional programming in purrr
Now that we have seen concepts about R and functions, let's dive into functional programming with purrr.
2. High order functions
First, it is time to talk about high order functions.
In maths and computer science, functions that have one or more functions as input and output are called high-order functions. In other words, they are functions that manipulate functions.
Here, for example, we are creating a nop_na() function. This function takes another function, passed as the fun parameter, and returns another function.
Here, we are using the sd() function as an input to the nop_na() function. This newly created object, called sd_no_na(), will run the function specified in fun, but with the na dot rm argument set to TRUE.
3. Three types of high order functions
Let's follow the taxonomy of high-order functions used in the "Advanced R" book.
First of all, there are the functionals. These functions take another function and return a vector. This is what map() and friends do: you provide it a list and a function, and the output is a list.
Function factories take a vector as input and create a function.
Function operators, finally, take one or more functions and return a function as output. This special kind is also called "adverbs" in purrr, and we will focus on this type in this lesson.
Why adverbs? In the tidyverse terminology, functions that take data and compute a value are called verbs. As function operators take a function as input and return this function modified (so they modify a verb), they can be considered as adverbs.
4. Adverbs in purrr
In purrr, there are several adverbs.
We won't cover them all in this chapter: we'll start slowly with the two adverbs which are dedicated to dealing with errors: possibly() and safely(). We will cover possibly() in the next lesson.
As you can see, the input of safely() is a function, here the mean() function, as is the output, safe_mean().
5. Use safely() to handle error.
One downside of iteration is that if the process fails on the last iteration, you won't have access to the result, and will need to restart the process. This can be a problem in several cases: for example, if the iteration takes a long time to run and rerunning it will be time-consuming, or if the iteration calls a web service with limited access. These cases can be solved by using safely().
When we call safely() with a function, the returned object is a new function. When we call this newly created function, the result will always work and will be a list of two objects: $result and $error.
One of them is NULL, depending on whether or not the function call succeeded: if the function call returned a result, the result element is filled and error is NULL, and vice versa if the function call failed.
6. Use safely() to handle error
This newly created safe function can then be used inside a map() call.
Compare the two here: with map() and log() combined, we get an error and no results. If we use safely(log), we can iterate over a list safely, and get the result when there is one. Imagine if our first map() had taken 8 hours, and just fails on the last iteration, returning only the error, we would have to start everything over.
7. Extracting elements from `safely()` results
Remember that you can use a character vector in the dot f part of map(). When doing this, you're asking map() to extract all the named elements from the list that match dot f.
So if you're using "result" or "error" on the output of the safely() call, you can extract either the results or the errors.
8. Let's practice!
Let's try to use this newly learned function on examples!