source(file.path(usethis::proj_get(), "vignettes", "_common.R"))
events$stop_not_useful("NullObject")
Null Object provides special behaviour for particular cases.
warning("The Null Object is not the same as the reserved word in R `NULL` (all caps).")
When a function fails in R, some functions produce a run-time error while others
return NULL
(and potentially prompt a warning). What the function evokes in
case of a failure is subjected to its programmer discretion. Usually, the
programmer follows either a punitive or forgiving policy regarding how run-time
errors should be handled.
In other occasions, NULL
is often the result of unavailable data. This could
happened when querying a data source matches no entries, or when the system is
waiting for user input (mainly in Shiny).
If it is possible for a function to return NULL
rather than an error, then it
is important to surround it with null test code, e.g.
if(is.null(...)) do_the_right_thing()
. This way the software would do the
right thing if a null is present.
Often the right thing is the same in many contexts, so you end up writing similar code in lots of places—committing the sin of code duplication.
Instead of returning NULL
, or some odd value such as NaN
or logical(0)
,
return a Null Object that has the same interface as what the caller expects.
In R, this often means returning a data.frame
structure, i.e. column names and
variables types, with no rows.
tryCatch
that returns the Null Object in the case of
an error:# Simulate a database that is 5% likely to fail read_mtcars <- function() if (runif(1) < 0.05) stop() else return(mtcars) # mtcars null object constructor NullCar <- function() mtcars[0, ] # How does the null car object look like? NullCar() # Subroutine with gracefully failing strategy set.seed(1814) cars <- tryCatch( # Try reading the mtcars dataset read_mtcars(), # If there is an error, return the Null Car object error = function(e) { return(NullCar()) } ) # Notice: Whether the subroutine fails or succeeds, it returns a tibble with # the same structure. colnames(cars)
geom_null <- function(...) { ggplot2::ggplot() + ggplot2::geom_blank() + ggplot2::theme_void() } if (exists("user_input")) { ggplot2::ggplot(user_input, ggplot::aes(x = mpg, y = hp)) + ggplot2::geom_point() } else { geom_null() + geom_text(aes(0, 0), label = "choose an entry from the list") }
classes <- function(x) sapply(x, class) test_that("mtcars follows a certain table structure", { # Compare column names expect_identical(colnames(mtcars), colnames(NullCar())) # Compare variable types expect_identical(classes(mtcars), classes(NullCar())) })
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.