falsy
falsy copied to clipboard
R package for falsy and truthy values
falsy
falsy defines falsy and truthy values. You might be familiar with them in other dynamic laguanges with prevalent implicit type conversions, e.g Python, Ruby, JavaScript and Lisp.
These languages typically define a set of values that are considered as false when used as a condition, and everything else is considered as true. The falsy package does the same for R.
The following R values are considered to be falsy:
-
NULL
-
FALSE
-
0L
, the integer zero value. -
0
, the real zero value. -
0+0i
, the complex zero value. -
""
empty string character scalar. -
00
one byte raw vector with zero value. - Empty vectors:
logical()
,integer()
,double()
,complex()
,character()
andraw()
. - Empty lists:
list()
. - Objects from the
try-error
class.
Note that the value must be completely identical to one of the listed ones to be falsy. E.g. an empty vector with an attribute is not falsy any more.
Everything else is truthy. In particular, everything with a class attribute is truthy. So empty vectors are falsy, but empty matrices are truthy.
Functions
The is_falsy
and is_truthy
functions simply decide if a value is falsy
or truthy.
library(falsy)
is_falsy(0)
## [1] TRUE
is_falsy(list())
## [1] TRUE
is_falsy("")
## [1] TRUE
is_truthy(0)
## [1] FALSE
is_truthy("0")
## [1] TRUE
is_truthy(matrix(nrow=0, ncol=0))
## [1] TRUE
Robust short-circuited logical operators
It is common to use truthy and falsy values with the short-circuited logical and and or operators, becuase the code will be short and (usually) readable. E.g. one can check if a vector has elements or give an error message:
v <- 1:5
length(v) > 0 || stop("empty v")
## [1] TRUE
v <- c()
length(v) > 0 || stop("empty v")
## Error in eval(expr, envir, enclos): empty v
Unfortunately, the ||
and &&
operators fail on values that cannot be
converted to a logical, using a (non-extendable) set of implicit conversion
rules. The falsy package privides the %||%
and %&&%
operators that are
essentially identical to ||
and &&
, but work with truthy
and falsy
values. This allows writing:
v <- 1:5
v %||% stop("empty v")
## [1] 1 2 3 4 5
v <- c()
v %||% stop("empty v")
## Error in eval(rhs, envir = parent.frame()): empty v
and more importantly also
l <- list(a = 1, b = 2)
l$a %||% stop("no a in l")
## [1] 1
l$c %||% stop("no c in l")
## Error in eval(rhs, envir = parent.frame()): no c in l
This works, because for non-existing keys lists return NULL
, which is
falsy.
The left or right hand sides of the %||%
and %&&%
operators can be
arbitrary R expessions. E.g. to shift a vector to zero, if it is not empty,
one can write
v <- 5:10
v %&&% { v <- v - min(v) }
## [1] 0 1 2 3 4 5
v
## [1] 0 1 2 3 4 5
v <- numeric()
v %&&% { v <- v - min(v) }
## numeric(0)
which is somewhat simpler than writing
if (length(v) > 0) v <- v - min(v)
Errors and try
Errors returned by try
are also falsy, which helps writing fallback
solutions.
col <- try(colorspace::rainbow_hcl(5)) %||% rainbow(5)
col
## [1] "#E495A5" "#BDAB66" "#65BC8C" "#55B8D0" "#C29DDE"
You probably want to suppress the misleading error message, whith is
possible with try_quietly
:
col2 <- try_quietly(colorspace::rainbow_hcl(5)) %||% rainbow(5)
col2
## [1] "#E495A5" "#BDAB66" "#65BC8C" "#55B8D0" "#C29DDE"
Negation
The nay
function returns a falsy value if its argument is truthy and
vice versa. The following code checks if a directory is empty:
dir.create(tmp <- tempfile())
nay(dir(tmp, all.files = TRUE, no.. = TRUE)) %||% message("Not empty")
## [1] TRUE
cat("Hello!", file = file.path(tmp, "foo"))
nay(dir(tmp, all.files = TRUE, no.. = TRUE)) %||% message("Not empty")
## Not empty
## NULL
Working with magrittr pipes
falsy goes well with the pipe operator of the
magrittr
package. In particular,
a falsy or truthy value in pipe can be replaced with another value
using %||%
and %&&%
:
library(magrittr)
vect <- 1:10
vect %>%
sum() %||% "foo" %>%
paste("!!!")
## [1] "55 !!!"
vect <- c()
vect %>%
sum() %||% "foo" %>%
paste("!!!")
## [1] "foo !!!"
Warnings
FALSE and falsy are not the same
From the definition of falsy values, it might not be obvious, but there are some values that R considers true, are falsy, and some values that R considers false are truthy.
The most notable example is objects with attributes. R does not
care about the presense of attributes in the condition of the if
construct,
but falsy
does:
obj <- matrix(FALSE, 1, 1)
if (!obj) "false"
## [1] "false"
is_falsy(obj)
## [1] FALSE
Vectors
falsy behaves somewhat surprisingly with vectors. Empty vectors are falsy,
but some of the non-empty ones are truthy, the most obvious example beging
FALSE
, which is also a length one logical vector. The others are 0
,
0L
, ""
, 0+0i
and as.raw(0)
. (See at the beginning.) This means that you
cannot reliably use %||%
to check if a vector is empty. You need to check
the legth of the vector explicitly.
empty <- c()
non_empty1 <- c("foo", "bar")
non_empty2 <- c("")
is_falsy(empty)
## [1] TRUE
is_falsy(non_empty1)
## [1] FALSE
is_falsy(non_empty2)
## [1] TRUE
This is incorrect:
non_empty2 %||% stop("empty vector!")
## Error in eval(rhs, envir = parent.frame()): empty vector!
The correct way to test if a vector is empty is to test if its length is zero:
length(non_empty2) %||% stop("empty vector!")
## [1] 1