In R, operations with booleans are evaluated in a short-circuit manner. This basically means that

if (x && y)
{
  # do something
}

is equivalent to

if (x) 
{
  if (y)
  {
    # do something
  }
}

that is, if x is not true, y is not evaluated anymore. This seems to be common to other programming languages which use lazy evaluation.

Here a few examples:

# the left-hand side is FALSE, no need to evaluate NA 
# see also help("&&")
FALSE && NA
# FALSE

# the left-hand side is TRUE, so NA gets evaluated this time
TRUE && NA
# NA

is_awake <- function()
{
  message("still sleeping")
  FALSE
}

is_happy <- function()
{
  message("being happy")
  TRUE
}

# is_happy is TRUE => is_awake also needs to be checked
if (is_happy() && is_awake()) {
  message("iupi")
}
# being happy
# still sleeping

# is_awake is FALSE, so no need to check if is_happy
if (is_awake() && is_happy()) {
  message("iupi")
}
# still sleeping

Due to short-circuit evaluation, you can theoretically speed up your programs if you put on the left side variables that are less likely to be true (compared to the right-side variables).

As I was familiar to this concept, I naively assumed that this happens all the time in R. So the following was a surprise to me:

dt <- data.table::data.table(
  value_type = c("numeric", "numeric", "string"),
  value = c("41", "42", "a")
)
dt[value_type == "numeric" & as.numeric(value) == 42]
# value_type value
# 1:    numeric    42
# Warning message:
#   In eval(.massagei(isub), x, ienv) : NAs introduced by coercion

Even if all the rows in dt with value_type == "numeric" can be successfully converted to a number, a warning is issued, because the value "a" cannot be converted into numeric, so short-circuit evaluation does not work inside a data.table. My guess is that it has to do with the way evaluation is done inside a data.table, and probably related to the fact that the heavy part of the data.table code is in C, which does not use short-circuit evaluation. This is a tiny price to pay for the efficiency of data tables.

You might want to suppress the warning in the code above, since you (kind of) understand where it comes from. I would however never suppress all the warnings -  they usually are (or should be) a useful tool, trying to make you pay more attention. I rather suggest something like this:

suppress_specific_warning <- function(expr, pattern)
{
  checkmate::assertString(x = pattern)
  
  withCallingHandlers(
    expr = expr,
    warning = function(w)
    {
      if (grepl(pattern = pattern, w$message)) {
        tryInvokeRestart("muffleWarning")
      }
    }
  )
}

suppress_specific_warning(
  pattern = "NAs introduced by coercion",
  {
    dt[value_type == "numeric" & as.numeric(value) == 42]
  }
)
#    value_type value
# 1:    numeric    42

If you want to know more about warnings and restarts, you might want to have a look here.

As I learned on the way, I am not alone in believing wrong things. Here two examples:

 

Make a promise. Show up. Do the work. Repeat.