+ - 0:00:00
Notes for current slide
Notes for next slide

Advanced R

Chapter 8: Conditions

Shel Kariuki @Shel_Kariuki

2020/10/05

1 / 41

Outline

  • Section 8.1: Introduction
2 / 41

Outline

  • Section 8.1: Introduction

  • Section 8.2: Signalling conditions

3 / 41

Outline

  • Section 8.1: Introduction

  • Section 8.2: Signalling conditions

  • Section 8.3: Ignoring conditions

4 / 41

Outline

  • Section 8.1: Introduction

  • Section 8.2: Signalling conditions

  • Section 8.3: Ignoring conditions

  • Section 8.4: Handling conditions

5 / 41

Outline

  • Section 8.1: Introduction

  • Section 8.2: Signalling conditions

  • Section 8.3: Ignoring conditions

  • Section 8.4: Handling conditions

  • Section 8.5: Custom conditions

6 / 41

Outline

  • Section 8.1: Introduction

  • Section 8.2: Signalling conditions

  • Section 8.3: Ignoring conditions

  • Section 8.4: Handling conditions

  • Section 8.5: Custom conditions

  • Section 8.6: Applications

7 / 41

Outline

  • Section 8.1: Introduction

  • Section 8.2: Signalling conditions

  • Section 8.3: Ignoring conditions

  • Section 8.4: Handling conditions

  • Section 8.5: Custom conditions

  • Section 8.6: Applications

  • Section 8.7: Quizzes

8 / 41

8.1: Introduction

The condition system provides a paired set of tools that allow the author of a function to:

  • indicate that something unusual is happening,

  • and the user of that function to deal with it.

The function author signals conditions with functions like stop() (for errors), warning() (for warnings), and message()

Then the function user can handle them with functions like tryCatch() and withCallingHandlers()

9 / 41

8.2: Signalling conditions

There are three conditions that can be signaled in code:

  • Errors: They indicate that the function cannot execute at its current state.

  • Warnings: Indicates that something has gone wrong, but that the function has kind of recovered (or has it?????)

  • Messages: They give more information of actions that are performed on behalf of the user.

stop("Hi there, I am an error.")
## Error in eval(expr, envir, enclos): Hi there, I am an error.
warning("Hi there, be careful when you see me")
## Warning: Hi there, be careful when you see me
message("I am better than them, aint I?")
## I am better than them, aint I?
10 / 41

8.2.1 Errors

In base R, errors are signalled by stop().

my_function1 <- function()stop("Error! Error! Error!")
my_function1()
## Error in my_function1(): Error! Error! Error!
my_function1 <- function() stop("Error! Error! Error!", call. = FALSE)
my_function1()
## Error: Error! Error! Error!

In rlang, they are signalled by rlang::abort()

my_function3 <- function()rlang::abort("rlang's error handling")
my_function3()
## Error: rlang's error handling
11 / 41

You cannot have multiple error signals in one function.

my_function123 <- function(){
stop("Error! Error! Error!")
stop("Error! Error! Error!", call. = FALSE)
rlang::abort("rlang's error handling")
}
my_function123()
## Error in my_function123(): Error! Error! Error!
12 / 41

8.2.2 Warnings

Indicates that something has gone wrong, but the function has kind of recovered.

Unlike errors, you can have multiple warnings from a single function call.

my_function4a <- function(){
cat("R4DS Cohort 3")
warning("I am the first warning")
cat("R4DS Cohort 3 again")
warning("I am the second warning")
}
my_function4a()
## R4DS Cohort 3
## Warning in my_function4a(): I am the first warning
## R4DS Cohort 3 again
## Warning in my_function4a(): I am the second warning

By default, warnings are cached and printed only when control returns to the top level.

13 / 41

One can control this behaviour with the warn option

  • options(warn = 0): default
  • options(warn = 1): makes warnings appear immediately.
options(warn = 1)
my_function4b <- function(){
cat("R4DS Cohort 4")
warning("I am the first warning")
cat("R4DS Cohort 4")
warning("I am the second warning")
}
my_function4b()
## R4DS Cohort 4
## Warning in my_function4b(): I am the first warning
## R4DS Cohort 4
## Warning in my_function4b(): I am the second warning
  • options(warn = 2): turns warnings into errors
14 / 41

We can set warnings in rlang with rlang::warn()

my_function4c <- function() rlang::warn("This is also a warning")
my_function4c()
## Warning: This is also a warning

Some warnings should be more of errors than warnings

my_vec1 <- c(15, 16, 35, "50+")
as.numeric(my_vec1)
## Warning: NAs introduced by coercion
## [1] 15 16 35 NA
my_vec2 <- c(15, 16, 35, "5 7")
as.numeric(my_vec2)
## Warning: NAs introduced by coercion
## [1] 15 16 35 NA
15 / 41

8.2.3 Messages

Messages are signalled by message().

They are informational; use them to tell the user that you’ve done something on their behalf.

Use messages to provide just enough information so the user knows what’s going on, but not so much that they’re overwhelmed

library(rKenyaCensus)

Generally any function that produces a message should have some way to suppress it.

One can use suppressMessages()

Comparison between cat() and message()

  • cat(): used when the primary role of the function is to print to the console. Used when the user asks for something to be printed.

  • message(): as a side-channel to print to the console when the primary purpose of the function is something else. Used when the developer elects to print something.

16 / 41
my_func5 <- function(){
cat("I want my function to print this")
message("This function contains a message.")
}
my_func5()
## I want my function to print this
## This function contains a message.
17 / 41

8.3: Signalling conditions

The simplest way of handling conditions in R is to simply ignore them:

  • Ignore errors with try().

try() allows execution to continue even after an error has occurred.

my_func6a <- function(x,y){
x+y
cat("Nairobi is the capital city of Kenya")
}
my_func6a(2)
## Error in my_func6a(2): argument "y" is missing, with no default
my_func6b <- function(x,y){
try(x+y)
cat("Nairobi is the capital city of Kenya")
}
my_func6b(2)
## Error in try(x + y) : argument "y" is missing, with no default
## Nairobi is the capital city of Kenya
18 / 41

Unlike errors, messages and warnings don’t terminate execution.

  • Ignore warnings with suppressWarnings()
suppressWarnings(warning("I am a warning, but I am just about to be supressed"))
  • Ignore messages with suppressMessages()
suppressMessages({
message("I am a message, but I am just bout to be supressed")
6^2
})
## [1] 36
19 / 41

8.4: Handling conditions

Every condition has default behaviour:

errors stop execution and return to the top level

warnings are captured and displayed in aggregate

messages are displayed immediately

Condition handlers allow us to temporarily override or supplement the default behaviour.

tryCatch() and withCallingHandlers() allow us to register handlers, functions that take the signalled condition as their single argument.

tryCatch() defines exiting handlers; after the condition is handled, control returns to the context where tryCatch() was called. This makes tryCatch() most suitable for working with errors and interrupts, as these have to exit anyway.

withCallingHandlers() defines calling handlers; after the condition is captured control returns to the context where the condition was signalled. This makes it most suitable for working with non-error conditions

20 / 41

8.4.1 Condition objects

Built-in conditions are lists with two elements:

  • message, a length-1 character vector containing the text to display to a user. To extract the message, use conditionMessage(cnd).

  • call, the call which triggered the condition. We extract it using conditionCall(cnd).

cnd <- rlang::catch_cnd(stop("This is an error!!"))
str(cnd)
## List of 2
## $ message: chr "This is an error!!"
## $ call : language force(expr)
## - attr(*, "class")= chr [1:3] "simpleError" "error" "condition"
21 / 41

8.4.2 Exiting handlers: tryCatch()

tryCatch() registers exiting handlers, so it is used to handle error conditions.

Exiting handlers because they cause code to exit once the condition has been caught.

Syntax: tryCatch(my_code,my_error_function)

my_code : piece of code we are working on.

my_error_function: function that contains the code to run when an error is thrown

my_code <- function(x, y){
x+y
}
my_code(2,"Shel")
## Error in x + y: non-numeric argument to binary operator
22 / 41

The handler functions are called with a single argument, the condition object (cnd).

my_func <- function(x, y){
tryCatch(
x+y,
error = function(cnd) cat("An error has occured")
)
}
my_func(2,"Shel")
## An error has occured
23 / 41

8.4.3 Calling handlers: withCallingHandlers()

withCallingHandlers() sets up calling handlers: code execution continues normally once the handler returns

my_func2 <- function(){
withCallingHandlers(
message("Omg!!, I now get why this book is titled 'Advanced R'"),
message = function(cnd){
message("This package contains a message, please read it carefully")
}
)
}
my_func2()
## This package contains a message, please read it carefully
## Omg!!, I now get why this book is titled 'Advanced R'
24 / 41
my_func3 <- function(){
withCallingHandlers(
as.numeric(c(2,3,"5 6")
),
warning = function(cnd){
warning("A warning has been signalled!!!", call. = FALSE)
}
)
}
my_func3()
## Warning: A warning has been signalled!!!
## Warning in withCallingHandlers(as.numeric(c(2, 3, "5 6")), warning = function(cnd)
## {: NAs introduced by coercion
## [1] 2 3 NA
25 / 41

??muffle

26 / 41

8.4.4 Call stacks

27 / 41

8.4.5 Exercise

28 / 41

8.5: Custom conditions

One of the challenges of error handling in R is that most functions generate one of the built-in conditions, which contain only a message and a call

We can create custom conditions that can contain additional metadata.

rlang::abort() makes this very easy as you can supply a custom .subclass and additional metadata.

29 / 41

8.5.1 Motivation

my_func <- function(){
as.numeric(c(34, 56, "7 13"))
}
my_func()
## Warning in my_func(): NAs introduced by coercion
## [1] 34 56 NA
my_func <- function(x){
withCallingHandlers(
as.numeric(x),
warning = function(cnd){
if(is.character(x)){
warning("There is a character value in your vector")
}
}
)
}
my_func(c("7 13"))
## Warning in (function (cnd) : There is a character value in your vector
## Warning in withCallingHandlers(as.numeric(x), warning = function(cnd) {: NAs
## introduced by coercion
## [1] NA

Custom conditions are more likely to guide the user towards a correct fix.

However, they’re no better if you want to programmatically handle the errors: all the useful metadata about the error is jammed into a single string.

30 / 41

8.5.2 Signalling

my_func <- function(x, y){
2 + x + y
}
my_func(3, "3")
## Error in 2 + x + y: non-numeric argument to binary operator
my_func <- function(x, y){
if(is.character(x) | is.character(y)){
rlang::abort("One of the arguments that you have provided is a character")
}
2 + x + y
}
my_func(3, "3")
## Error: One of the arguments that you have provided is a character
31 / 41
my_func <- function(x, y){
if(is.character(x) | is.character(y)){
rlang::abort(glue::glue({y}, " is a character"))
}
2 + x + y
}
my_func(3, "3")
## Error: 3 is a character
my_func <- function(x, y){
if(is.character(x) & !is.character(y)){
rlang::abort(glue::glue({y}, " is a character input."))
}else
if(is.character(y) & !is.character(x)){
rlang::abort(glue::glue({y}, " is a character input."))
}else
if(is.character(x) & is.character(y)){
rlang::abort(glue::glue({x}, " and " ,{y}, " are characters inputs."))
}
2 + x + y
}
my_func("3", "10")
## Error: 3 and 10 are characters inputs.
32 / 41

8.5.3 Handling

33 / 41

8.5.4 Exercises

34 / 41

8.6 Applications

8.6.1 Failure value

fail_with <- function(x,y, value = NULL) {
tryCatch(
error = function(cnd) value,
x * y
)
}
fail_with(2,"3", "You must have insert a character value")
## [1] "You must have insert a character value"
35 / 41

8.6.2 Success and failure values

do_with <- function(x,y, value = NULL) {
tryCatch(
error = function(cnd) value,
{
x * y
cat("Your inputs are perfect\n\n")
}
)
}
do_with(2,3, "You must have insert a character value")
## Your inputs are perfect
36 / 41

8.6.3 Resignal

37 / 41

8.6.4 Record

38 / 41

8.6.5 No default behaviour

39 / 41

8.6.6 Exercises

40 / 41
41 / 41

Outline

  • Section 8.1: Introduction
2 / 41
Paused

Help

Keyboard shortcuts

, , Pg Up, k Go to previous slide
, , Pg Dn, Space, j Go to next slide
Home Go to first slide
End Go to last slide
Number + Return Go to specific slide
b / m / f Toggle blackout / mirrored / fullscreen mode
c Clone slideshow
p Toggle presenter mode
t Restart the presentation timer
?, h Toggle this help
Esc Back to slideshow