```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, prompt = TRUE, comment = NA, collapse = TRUE)
```

# Background

In R, everything that happens is due to a function, and everything that exists is an object. Functions themselves are objects. Functional programming is about concepts related to functions working together. How do functions work together? We can classify functions according to their inputs and outputs: 

Input/Output | Data             | Function 
-------------|------------------|-----------------
Data         | Regular function | Function factory
Function     | Functional       | Function operator


These concepts are loosely defined, because functions can take both data and function arguments and return data and function results.

Two additional concepts we will cover are _closures_ which is a special type of function factory that is useful in statistics, and _recursion_. 

# Functionals

Functionals invoke functions over some data structure to create an output that corresponds to that data structure. We've already used one functional: `lapply`. Here is a simpler one:

```{r}
random_stat <- function(fun) {
    fun(runif(100))
}

random_stat(mean)

```

The data structure here is a single variable (`runif(100)`). Functionals are useful for iterating over more complex data structures as well. 

`lapply` iterates over a list, applying the same function to each element. How can you achieve the same result using a `for` loop?

```{r}
random_stats <- function(fun) {
    
    lapply(list(uniform = runif(100), normal = rnorm(100), 
                t = rt(100, 4), exponential = rexp(100)), fun)
    
}

random_stats(mean)
random_stats(sd)

lapply(list(mean = mean, sd = sd), random_stats)
```

`mapply` iterates over a series of lists. Same question? 

All of these iteration problems can be solved with loops. What are the advantages/disadvantages of using loops? 

# Function operators

Function operators take functions as input and return functions as output, generally modifying the behaviour of the function. 

Let's see if we can implement an error capture adverb. 

```{r}
#' Modify a function to fail without stopping
#' 
#' @param f A function

capture_errors <- function(f) {
    
    function(...) {
        tryCatch(f(...), 
                 error = function(e) as.character(e))    
        
    }
}

geometric_mean <- function(x) {
    
    exp(mean(log(x)))
    
}

safe_geom_mean <- capture_errors(geometric_mean)
#lapply(iris, geometric_mean)
lapply(iris, safe_geom_mean)
```


# Function factories and closures

Function factories take data as input and return functions:

```{r}
power1 <- function(exp) {
  function(x) {
    x ^ exp
  }
}

square <- power1(2)
cube <- power1(3)
```

Function factories are useful in statistics. Let's do some regression!

```{r}
x <- rnorm(50)
y <- 1 + 1.5 * x + rnorm(50, sd = 1)

mse_loss <- function(xmat, outcome) {
    
    function(beta) {
        
        linpred <- xmat %*% beta
        mean((linpred - outcome)^2)
        
    }
    
}


mse_loss(cbind(1, x), y)(c(0, 0))

optim(par = c(0, 0), mse_loss(cbind(1, x), y))
```

What are some ways we could make this function more general? Multivariate, different loss functions? 

```{r}

general_loss <- function(xmat, outcome, fun) {
    
    function(beta) {
        
        linpred <- xmat %*% beta
        fun(linpred, outcome)
        
    }
    
}

mad <- function(x1, x2) {
  
  median(abs(x1 - x2))
  
}
mad_loss <- general_loss(cbind(1, x), y, mad)


optim(par = c(0, 0), mad_loss)
```

# Recursive functions

Recursive functions are functions that call themselves. It is critical that they have an exit condition. The basic example is computing the Fibonacci numbers: 

```{r}
fibonacci <- function(n) {
    
    if(n < 2){
        
        return(1)
        
    } else {
        
        fibonacci(n - 1) + fibonacci(n - 2)
    }
    
}

fibonacci(9)
```


This seems like a useless novelty, but they are actually quite useful in dealing with DAGs and nested data structures.



# Discussion

1. The pipe operator `%>%` from the `magrittr` package allows you to perform function composition in a clear, linear way. E.g., 

```{r}
library(magrittr)

set.seed(123)
mean(exp(rnorm(100)))

## becomes
set.seed(123)
rnorm(100) %>% 
    exp %>% 
    mean

```

Is function composition an example of functional programming? Why or why not?



