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

## Recap from the morning

Everything is done via (different types of) functions (see _functional programming_ tomorrow):

* data, parameters go in as _arguments_
* results are returned as _value_ of the function
* both are R objects

Functions have

* arguments: `args(lm)` or `formals(lm)`
* a body of code: `body(lm)`
* an evironment: `environment(lm)`

An argument 

* is evaluated lazily (when **and** if required)
* can be checked for presence via `missing` (if necessary)
* can be an ellipsis \ldots

Fun fact - you can overwrite body, arguments (via `formals`) and environment in an assignment:

```{r}
set.seed(313)
x = rnorm(10); y = 2 + 3* x + rnorm(10)/4
coef( lm(y~x) )     ## Show coefficients only
body(lm) = "No linear regression for you!"
lm(y~x) 
lm
```

Because the original `lm` is defined in a package, changes are done to a local copy in the current environment. At the command line, this is the global environment:

```{r}
find("lm")
coef( stats::lm(y~x) )   ## Still works 
rm(lm)                   ## Delete local copy
coef( lm(y~x) )          ## Works again
```

Lessons learned: 

1. We can have functions with the same name in the search path - someone keeps (magically?) track of them. 

2. There are differences between functions living in a package and in the global environment (e.g. generated via a script). 

# Getting {stuff} out of functions 

## Returning results

By default, an R function returns the value of the last statement in the body:

```{r}
boxcox = function(x, exp) {
  
  if (exp == 0) {
    ret = log(x)
  } else {
    ret = x^exp
  }
 
  ret 
}
```

A function can return any value at any point in the code by using the `return` function:

```{r}
boxcox = function(x, exp) {
  
  if (exp == 0) {
    return( log(x) )
  } else {
    return( x^exp )
  }
 
}
```
I think this makes programs harder to read and generally avoid using `return`; opinions on this differ.

`invisible` works like `return`, but the resulting value will not be printed:
```{r}
invisible_square = function(x)
{
  invisible(x * x)
}
invisible_square(2)
x = invisible_square(2)
x
```

## Complex return values

Often, a vector, matrix or data frame is the obvious return value (e.g. `boxcox`).

Sometimes, a matrix or data frame is not the most obvious, but still the most useful return value (e.g. `broom::tidy`).

And sometimes, you just get lots of different objects that you need to stick together, which is when you can use a list:

```{r}
lm_min = lm(1 ~ 1)    ## World's smallest linear model?! No, but close
is.list(lm_min)
sapply(lm_min, typeof)
```


## Designing return values

Return the same type of result every time (if possible). Unlike this:

```{r}
DNase[1:5, ]
DNase[1:5, 3]  ## use drop=FALSE to make safe
```

Consider tidy return values: does it make sense to return a rectangular data matrix?

```{r}
data(veteran, package = "survival")
tab  = with(veteran, table(celltype, status))
ttab = reshape2::melt(tab,  value.name= "Freq")
tab   ## NOT tidy
ttab  ## tidy
glm(Freq ~ celltype + status, data = ttab)  ## Statistics, yay
```

Avoid fancy printing and plotting, unless this is the only thing your function does. Many older functions do this: 

```{r, fig.keep = "none"}
hist(DNase$density)                    ## plot, invisible result
hist(DNase$density, plot = FALSE)      ## no plot - visible result
hh = hist(DNase$density, plot = FALSE) ## save object
plot(hh)                               ## ... and plot it
```
Compare this to `ggplot2`, which has a clean separation between defining and displaying a plot.

Function `effx` in package `Epi` produces fancy-ish text output, but returns nothing, _nothing at all_:
```{r}
data(births, package = "Epi")
out = Epi::effx(bweight,exposure=hyp,data=births)
out
```
Don't do that. 


## Communicating with the user

Functions should avoid elaborate print output unless this is the only thing they do: see above, object-oriented programming. 

Instead, functions should put construct an object with all the relevant information, and either trust the R printing methods, or hide the ugly using `invisible`. 

However, there are situations where it makes sense to contact the user from within the function:

1. Progress: for a slow process, use `cat` (not `print`) to indicate progress; offer an argument to suppress the progress report. 

    (Alt. use package `progress` - however, does not work on terminal R)

2. Side effects: if the function does something that will have side effects outside itself, the user needs to be informed. This should be done via `message` and `warning`, not print-ing or cat-ing.

3. Problems: if the function runs into a situation it cannot handle, it can either try a best-guess fix, or just give up; `warning` informs, but continues execution, `stop` terminates execution and returns an error.

```{r, eval = FALSE}
log(-1)      ## Warning, moves on with NaN
log("a")     ## Error
```

Useful:

* `stopifnot` for tough-love argument checking
* `options(warn=2)` to turn warnings into errors

# Functions of functions

## Functions as return values

Functions as arguments are common: `lapply` etc.

Functions can also be return values. E.g. we can define our own logarithms for different bases:

```{r}
log_general = function(base) 
{
  function(x) log(x, base)
}
log2 = log_general(2)
log2(4)
```

This `log2` is a function defined in `log_general`. When `log_general` was called with `base=2`, this was saved in the _execution environment_ of `log_general`, which `log2` has inherited:

```{r}
environment(log2)
ls( environment(log2) )
get( "base", environment(log2) ) 
```

This is a reasonably advanced, but powerful technique. This combination of a function and data (in the environment) is generally known as a _closure_, though the term has a narrower technical meaning in R. See Advanced R 2nd ed, Section 10.4, for use cases.

## Function wrappers

A different, more conventional way of defining our own binary logarithm:

```{r}
other_log2 = function(x) log(x, base = 2)
other_log2(4)
environment(other_log2)
```

Nothing wrong with that as one off, though ultimately less flexible. 

## Managing complaints

Other people's functions may also cause messages, warnings or errors. 

Use `suppressMessages` or `suppressWarnings` to keep it silent:

```{r}
suppressWarnings( log(-1) )
library(dplyr)
library(MASS) 
detach("package:MASS")
suppressMessages( library(MASS) )
```

Errors can be caught using the function `try`:

```{r, eval = FALSE}
try( log("a"), silent = TRUE )  ## Not an error, but what happened?!
## Convert error to warning, use NaN as value
ret = try( log("a"), silent = TRUE )
if ( !is.numeric (ret) ) {
  warning(ret)
  ret = NaN
}
```

`tryCatch` offers a more flexible way of defining error handlers.

# Exercises

1. What do these functions return?

```{r}
ex1_a = function() 7
ex1_b = function() x=7
ex1_c = function() {x=7}
ex1_d = function() {y=1; x=7}
ex1_e = function() y=1; x=7
```

2. Write a function `hasPlotArg` that takes a function as argument and checks whether the function has `plot` as an argument. 

    Find all functions in the search path that have `plot` in their name, and check which ones have a `plot`-argument.

    If we also want to know about the default value for argument `plot`, should we add an optional argument to `hasPlotArg`, or is there a better way to design this? Implement your idea. 
    
3. Have a look how the function `read.table` handles unexpected situations: when does it give up, and when does it continue reading with a message/warning?


