vasanthcullen
vasanthcullen

Reputation: 386

stopifnot() fails for one expression in ... and doesnt work as I expect it to. Are my expectations correct?

Description of stopifnot from R Documentation

Description

If any of the expressions in ... are not all TRUE, stop is called, producing an error message indicating the first of the elements of ... which were not true.

Usage

stopifnot(...)

Arguments

... any number of (logical) R expressions, which should evaluate to TRUE.

Sometimes the expressions in ... dont evaluate to TRUE. Consider the error message for the the below scenario

a <- 1:10
stopifnot(
  class(a) %in% c("numeric", "integer"),
  sum(a) >= 100
)

> Error: sum(a) >= 100 is not TRUE

which is fine. But, the error message for the the below scenario is not what I expect

a <- letters
stopifnot(
  class(a) %in% c("numeric", "integer"),
  sum(a) >= 100
)
> Error in sum(a) : invalid 'type' (character) of argument

I expect it to say

Error: class(a) %in% c("numeric", "integer") is not TRUE

But, it doesnt.

Are my expectations correct? Is that how stopifnot() is supposed to work? And how can I make stopifnot() work like that?

Upvotes: 1

Views: 762

Answers (2)

Hong Ooi
Hong Ooi

Reputation: 57686

stopifnot evaluates each of your supplied expressions, and then checks that all of them are TRUE. If this isn't the case, it halts and prints an error message, saying which of the expressions failed.

In your example, what's happening is that the expressions themselves are triggering an error. It's the evaluation of the expression sum(a) >= 100 that results in the error message, not stopifnot itself.

Upvotes: 2

vasanthcullen
vasanthcullen

Reputation: 386

I cant answer about how stopifnot() is supposed to work. But, here is how to achieve what is expected

Consider using the hadley's assertthat package. It works as expected

a <- letters
assertthat::assert_that(
  class(a) %in% c("numeric", "integer"),
  sum(a) >= 100
)

> Error: `%in%`(x = class(a), table = c("numeric", "integer")) is not TRUE

It also gives more useful error messages. The above error message is not a really good example of that. Consider the below scenario

a <- 1:10
assert_that(
  class(a) %in% c("numeric", "integer"),
  sum(a) >= 100
)

> Error: sum(a) not greater than or equal to 100

Also, here's how stopifnot() can be made to work like that. So, now for the same scenario

stopifnot                        <- function (...) {
  ll                             <- lazyeval::lazy_dots(...)
  n                              <- length(ll)
  if (n == 0L) {
    return(invisible())
  }

  mc                             <- match.call()
  for (i in 1L:n) {
    if (!(is.logical(r <- lazyeval::lazy_eval(ll[[i]])) && !anyNA(r) && all(r))) {
      ch <- deparse(mc[[i + 1]], width.cutoff = 60L)
      if (length(ch) > 1L) {
        ch <- paste(ch[1L], "....")
      }
      stop(sprintf(ngettext(length(r), "%s is not TRUE", "%s are not all TRUE"), ch), call. = FALSE, domain = NA)
    }
  }

  invisible()
}

a <- letters
stopifnot(
  class(a) %in% c("numeric", "integer"),
  sum(a) >= 100
)
> Error: class(a) %in% c("numeric", "integer") is not TRUE

Upvotes: 1

Related Questions