---
title: "Try Catch"
author: "Colin Fay"
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Try Catch}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
  error = TRUE,
  collapse = TRUE,
  comment = "#>"
)
library(attempt)
```

## attempt 

`attempt()` is a wrapper around base `try()` that allows you to insert a custom messsage on error. 

```{r}
attempt(log("a"))
# Error: non-numeric argument to mathematical function
attempt(log("a"), msg = "Nop !")
# Error: Nop !
```

You can make it verbose (i.e. returning the expression): 

```{r}
attempt(log("a"), msg = "Nop !", verbose = TRUE)
# Error in log("a"): Nop !
```

Of course the result is returned if there is one: 

```{r}
attempt(log(1), msg = "Nop !", verbose = TRUE)
# [1] 0
```

As with `try()`, the result can be saved as an error object: 

```{r}
a <- attempt(log("a"), msg = "Nop !", verbose = TRUE)
a
# [1] "Error in log(\"a\"): Nop !\n"
# attr(,"class")
# [1] "try-error"
# attr(,"condition")
# <simpleError in log("a"): Nop !>
```

You can check if the result is an error with `is_try_error()`

```{r eval = TRUE}
a <- attempt(log("a"), msg = "Nop !", verbose = FALSE)
is_try_error(a)
```


## silent_attempt 

`silent_attempt()` is a wrapper around `silently()` (see further down for more info) and `attempt()`. It attempts to run the expr, stays silent if the expression succeeds, and returns error or warnings if any. 

```{r}
silent_attempt(log("a"))
# Error: non-numeric argument to mathematical function
silent_attempt(log(1))
```


## try catch

You can write a try catch with these params: 

+ `expr` the expression to be evaluated 
+ `.e` a mapper or a function evaluated when an error occurs
+ `.w` a mapper or a function evaluated when a warning occurs
+ `.f` a mapper or an expression which is always evaluated before returning or exiting

In `.e` and `.f`, the `.x` refers to the error / warning object.

### With mappers 

```{r}
try_catch(
  expr = log("a"),
  .e = ~ paste0("There is an error: ", .x),
  .w = ~ paste0("This is a warning: ", .x)
)
# [1] "There is an error: Error in log(\"a\"): non-numeric argument to mathematical function\n"

try_catch(
  log("a"),
  .e = ~ stop(.x),
  .w = ~ warning(.x)
)
# Error in log("a") : non-numeric argument to mathematical function

try_catch(
  matrix(1:3, nrow = 2),
  .e = ~ print(.x),
  .w = ~ print(.x)
)
#<simpleWarning in matrix(1:3, nrow = 2): data length [3] is not a sub-multiple or multiple of the number of rows [2]>

try_catch(
  expr = 2 + 2,
  .f = ~ print("Using R for addition... ok I'm out!")
)
# [1] "Using R for addition... ok I'm out!"
# [1] 4
```

As usual, the handlers are set only if you call them: 

```{r}
try_catch(matrix(1:3, nrow = 2), .e = ~ print("error"))
#      [,1] [,2]
# [1,]    1    3
# [2,]    2    1
# Warning message:
# In matrix(1:3, nrow = 2) :
# data length [3] is not a sub-multiple or multiple of the number of rows [2]
```

```{r}
try_catch(matrix(1:3, nrow = 2), .w = ~ print("warning"))
# [1] "warning"
```


### Traditionnal way 

`{attempt}` is flexible in how you can specify your arguments. 

You can, as you do with `{base}` `tryCatch()`, use a plain old function: 

```{r}
try_catch(
  log("a"),
  .e = function(e) {
    print(paste0("There is an error: ", e))
    print("Ok, let's save this")
    time <- Sys.time()
    a <- paste("+ At", time, ", \nError:", e)
    # write(a, "log.txt", append = TRUE) # commented to prevent log.txt creation
    print(paste("log saved on log.txt at", time))
    print("let's move on now")
  }
)

# [1] "There is an error: Error in log(\"a\"): non-numeric argument to mathematical function\n"
# [1] "Ok, let's save this"
# [1] "log saved on log.txt at 2018-01-30 16:59:13"
# [1] "let's move on now"
```

You can even mix both: 

```{r}
try_catch(
  log("a"),
  .e = function(e) {
    paste0("There is an error: ", e)
  },
  .f = ~ print("I'm not sure you can do that pal !")
)
# [1] "I'm not sure you can do that pal !"
# [1] "There is an error: Error in log(\"a\"): non-numeric argument to mathematical function\n"

try_catch(
  log("a"),
  .e = ~ paste0("There is an error: ", .x),
  .f = function() print("I'm not sure you can do that pal !")
)
# [1] "I'm not sure you can do that pal !"
# [1] "There is an error: Error in log(\"a\"): non-numeric argument to mathematical function\n"
```

### `try_catch_df()`

`try_catch_df()` returns a tibble with the call, the error message if any, the warning message if any, and the value of the evaluated expression or "error". The values will always be contained in a list-column.

```{r eval = TRUE}
res_log <- try_catch_df(log("a"))
res_log
res_log$value

res_matrix <- try_catch_df(matrix(1:3, nrow = 2))
res_matrix
res_matrix$value

res_success <- try_catch_df(log(1))
res_success
res_success$value
```

### `map_try_catch()`

`map_try_catch()` and `map_try_catch_df()` allow you to map on a list of arguments `l`, to be evaluated by the function in `fun`. 

```{r eval = TRUE}
map_try_catch(l = list(1, 3, "a"), fun = log, .e = ~.x)

map_try_catch_df(list(1, 3, "a"), log)
```
