```{r}
rm(list = ls()) # clean-up workspace
sessionInfo()
```

## Announcement
 
 - **1-page** report of course project deadline extended to next Wednesday (because of Fall break)
 
 - No lab session this week.

## Functions

 - Functions are a fundamental building block of R
 
 - Functions are **objects** in their own right (so that they can have `attributes()`)
 
 - [Functional programming](http://adv-r.had.co.nz/Functional-programming.html#functional-programming)

### Function components

 - All R functions have three parts:

    - the `formals()`, the list of **arguments** which controls how you can call the function
 
    - the `body()`, the **code** inside the function
    
    - the `environment()`, the "map" of the location of the function's variables
    
```{r}
f <- function(x) x^2
f

formals(f)
body(f)
environment(f)
```


## Define a function

 - There is no special syntax for defining and naming a function
 
 - simply create a function object (with `function`) and bind it to a name with `<-`

```{r}
DoNothing <- function() {
  return(invisible(NULL))
}
DoNothing()
```

## Invoke a function

 - You normally call a function by placing its arguments, wrapped in parentheses, after its name:
 
```{r}
mean(1:10, na.rm = TRUE)
```
 
 - What if you have the arguments already in a data structure?
 
```{r}
args <- list(1:10, na.rm = TRUE)
do.call(mean, args)
```
 
 - You can use `do.call()`.
 
## Lexical scoping

 - Now let's discuss **scoping**
 
 - R uses **lexical scoping** that follows four primary rules:
 
   - Name masking
   
   - Functions versus variables
   
   - A fresh start
   
   - Dynamic lookup
   

### Name masking

 - Names defined inside a function **mask** names defined outside a function
 
```{r}
x <- 10
y <- 20

g02 <- function(){
   x <- 1  # a local variable to the function
   y <- 2
   c(x, y)
}
g02()
```
 
 - If a name isn't defined inside a function, R looks one level up.
 
```{r}
x <- 2
g03 <- function() {
   y <- 1
   c(x, y)
}
g03()
y
```

 - R searches inside the current function, then looks where the function is defined and so on, all the way up to the global environment.
 
 - Finally, R looks in other loaded packages.

```{r}

y <- 10

f <- function(x) {
   y <- 2
   y^2 + g(x)
}

g <- function(x) {
   x * y
}

```

What is the value of `f(3)`?

### functions versus variables

 - In R, functions are ordinary objects.  This means the scoping rules described above also apply to functions.
 
 - However, rules get complicated when functions and non-functions share the same name.
 
 - Better avoid assigning same names to objects 
 
### A fresh start

 - Every time a function is called a _new_ environment is created to host its execution.

```{r}
g11 <- function() {
  if (!exists("a")) {
    a <- 1
  } else {
    a <- a + 1
  }
  a
}

g11()
g11()
```

> What happens if we do 

```{r, eval=FALSE}
a <- 1:5
g11()
g11()

```

### Dynamic lookup

 - Lexical scoping determines where to look for values.
 
 - R looks for values when the function is run, not when the function is created.

```{r}
g12 <- function() x + 1
x <- 15
g12()

x <- 20
g12()
```

 - Depending on variables defined in the global environment can be bad!

 - `codetools::findGlobals()` can be helpful
 
### Default arguments

 - You can define default values for arguments
 
 - Default values can be in terms of other arguments, or even in terms of variables defined later in the function
 
 - This is because R uses **Lazy Evaluation** that function arguments are only evaluated if accessed.
 
```{r}
h04 <- function(x = 1, y = x * 2, z = a + b) {
  a <- 10
  b <- 100
  
  c(x, y, z)
}

h04()
```

### `...` (dot-dot-dot)

 - Functions can have a special argument `...`
 
 - With `...`, a function can take any number of additional arguments
 
 - You can use `...` to pass those additional arguments on to another function
 
 **Pro**
   
   - If your function takes a function as an argument, you want some way to pass additional arguments to that function.

```{r}
x <- list(c(1, 3, NA), c(4, NA, 6))
str(lapply(x, mean, na.rm = TRUE))   
```
   
 **Con**
 
   - A misspelled argument will not raise an error.

```{r}
sum(1, 2, NA, na_rm = TRUE)
```
 
## Control flow

These are the basic control-flow constructs of the R language. They function in much the same way as control statements in any Algol-like (Algol short for "Algorithmic Language") language. They are all reserved words.

| keyword | usage |
| ------- | ----- |
| if | **if**(_cond_) _expr_ |
| if-else | **if**(_cond_) _cons.expr_ **else** _alt.expr_ |
| for  | **for**(_var_ **in** _seq_) _expr_ |
| while | **while**(_cond_) _expr_ |
| break | breaks out of a _for_ loop |
| next | halts the processing of the current iteration and advances the looping index|

## Exiting a function

Most functions exit in one of two ways:

  - return a value, indicating success
 
  - throw an error, indicating failure
 
### Implicit versus explicit returns

There are two ways that a function can return a value:

  - Implicitly, where the last evaluated expression is the return value:
  
```{r}
j01 <- function(x) {
  if (x < 10) {
    0
  } else {
    10
  }
}
j01(5)
j01(15)
```
  
  - Explicitly, by calling `return()`
  
```{r}
j02 <- function(x) {
  if (x < 10) {
    return(0)
  } else {
    return(10)
  }
}
```

  - You can hide the output from automatic printing by applying `invisible()` to the last value:
  
```{r}
j04 <- function() invisible(1)
j04()
```

### Errors

If a function cannot complete its assigned task, it should throw an error with `stop()`, which immediately terminates the execution of the function.

```{r, error=TRUE}
j05 <- function() {
  stop("I'm an error")
  return(10)
}
j05()
```

### Exit handlers

  - Use `on.exit()` to set up an **exit handler** that is run regardless of whether the function exits normally or with an error
  
  - Always set `add = TRUE` when using `on.exit()`. Otherwise, each call will overwrite the previous exit handler.
  
```{r, error=TRUE}
j06 <- function(x) {
  cat("Hello\n")
  on.exit(cat("Goodbye!\n"), add = TRUE)
  
  if (x) {
    return(10)
  } else {
    stop("Error")
  }
}

j06(TRUE)

j06(FALSE)
```

  - Can use exit handler for clean-up
  
```{r, error = TRUE}
with_dir <- function(dir, code) {
  old <- setwd(dir)
  on.exit(setwd(old), add = TRUE)

  code
}

getwd()
with_dir("~", getwd())
getwd()
```


