---
title: "Breaking Functions"
author: "JJB + Course"
output:
   html_document:
     toc: true
     toc_float:
       collapsed: false
---

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

# Ubiquitousness of Functions

## Example Recall: Add

```{r sample-function}
add = function(x, y) {
  return(x + y)
}

add(1, 3)
```

## Functions as Objects

```{r function-as-object}
square = function(x) {  # Create a square function
  x^2
}

class(square)            # Find high-level class information
typeof(square)          # Obtain low-level class information
```

## Extracting Function Properties

```{r breaking-apart-function}
formals(square)          # Retrieve parameters & default values
body(square)               # Retrieve the function body
environment(square) # Retrieve the location of function
```

## Hidden Function Calls

```{r hidden-calls}
# Addition
10 + 25

`+`(10, 25)

`-`(5, 10)

# Assignment
x = c(1, 2, 3)
`=`(x, c(1, 2, 3))

# Subset
x[[1]] 
`[[`(x, 1)
```

## Anonymous Functions

```{r anon-function}
function(x = 4) x + 1

(function(x = 4) x + 1)(2)      # Anonymous definition

add_one = function(x = 4) x + 1 # Named function 
add_one(2) 
```

## Function as a Parameter

```{r function-as-object-param}
add = function(x, y) { x + y }
subtract = function(x, y) { x - y }
multiply = function(x, y) { x * y }

ops = function(f, x, y) { 
    f(x , y)
}

ops(add, 2, 5)

ops(subtract, 2, 5)
```

### Exercise:

Determine the function properties of `mean()`

```{r viewing-mean}
# The parameters of the function

# Look at the definition of the function
# Definition is the body statements

# This is the traditional definition
# but, it uses an S3 class to handle different kinds
# of data types.
#
# This requires more specification of what is happening.

# Pull up help docs
# ?mean
```

# Asserting Input

## Example: Maligned input

```{r naive-logit-imp}
logit = function(p) { 
    log(p/(1 - p))
}

logit(.5)

logit(.75)

logit(1.5)

logit(-.5)
```

This will trigger an error during the kniting. Why does it trigger an error?

```{r char-input-issue, error = TRUE}
logit("a")
```

## Example: Protected Input

```{r logit-safe-implementation}
logit_safe = function(p) { 
    if(!is.numeric(p)) {
        stop("`p` must be numeric", call. = FALSE)
    } else if (any(p >= 1 )) {
        stop("All `p` values must be less than 1", call. = FALSE)
    } else if (any(p <= 0)) {
        stop("All `p` values must be greater than 0", call. = FALSE)
    } 
    log(p/(1 - p))
}

logit_safe(.5)

logit_safe(.75)
```

By specifying `error = TRUE` in the code chunk option, the
"knit" to HTML will _not_ fail as the error state will be _saved_. 

```{r logit-safe-error, error = TRUE}
logit_safe(1.5)

logit_safe(-.5)

logit_safe("a")
```

Though, an alternative is to disable the code chunk via the
traditional `eval = FALSE` option. 

```{r logit-safe-eval, eval = FALSE}
logit_safe(1.5)

logit_safe(-.5)

logit_safe("a")
```

## Exercise: Z-score

1. Identify what is wrong with the following function 
2. Propose a fix for it

```{r bad-z-implementation}
z_score = function(x, mu, std) {
  (x - mu) /  std
}
```

# Debugging Errors

## Example: Error Nested Function

```{r perform-computation, error = TRUE}
f = function(x) {
  g(x + 2)
}

g = function(x) {
  h(x - 2)
}

h = function(x) {
  val = log(x)
  if(val < 0) {
    return(val^2)
  }
  val
}
f(-4)
```

```{r show-traceback}
# if RStudio is not available
traceback()
```

Now, we can understand the error by manually re-creating the logic:

```{r calculation-woes, error = TRUE}
-4 + 2 + # this is done in f(x)
  -2 # done in g(x)

# Retrieve NaN: Not a Number
log(-4)
```

If we allow this to propogate into the _if_ statement, it becomes
problematic. 

```{r bad-if, error = TRUE}
# if( <condition> ) { message("TRUE STATEMENT ")} else { message("FALSE STATEMENT")}
if( log(-4) > 0 ) { message("TRUE!")}

if(NA) { message("Did it work?")}

# Complex numbers
# ((1 + 5i) + (2 - 2i)) > 3

#@ Re(1+5i)
```


## Example: Print Trace Statements Added

```{r add-print-trace, error = TRUE}
f = function(x) {
  message("value of x in f(): ", x)       # Added message
  g(x + 2)
}

g = function(x) {
  message("value of x in g(): ",x)       # Added message
  h(x - 2)
}

h = function(x) {
  message("value of x in h(): ", x)       # Added message
  val = log(x)
  message("value of val in h(): ",val)     # Added message
  if(val < 0) {
    return(val^2)
  }
  val
}
f(-4)
```

## Example: Continued...

For additional examples, please see the `lec27-*.R` files as 
some features require direct use of _R_ Scripts instead of RMarkdown.

