dplyr::if_else() doesn't run a function correctly as an outcome, whereas base ifelse() does perfectly. Why?

Viewed 118

I understand the primary difference between dplyr::if_else() and base ifelse(): strict type-checking between true and false outcomes in the former.

However I have noticed another difference, and I'm wondering if it's the intended behaviour or if I am just doing it wrong.

Here's some simple code:

library(dplyr)

choose_number <- function() {
  n <- readline("Choose a number between 1 and 4: ")
  n <- as.integer(n)
  if (between(n, 1, 4)) { return(n) }
  else { return(NA_integer_) }
}

# base ifelse() - works perfectly
get_answer <- function(pick = FALSE, n = 2) {
  n <- ifelse(pick, choose_number(), as.integer(n))
  return(n)
}

# dplyr::if_else() - does not work in the same way
get_answer <- function(pick = FALSE, n = 2) {
  n <- if_else(pick, choose_number(), as.integer(n))
  return(n)
}

I want get_answer() to call choose_number() only when the pick argument is TRUE. Otherwise it should just return the value of n. It does this perfectly with base ifelse() in the code above, but not with the dplyr version.

The dplyr version does not return an error or warning, however; it still calls choose_number() but ignores the result and just returns the default value of n.

The true option for if_else, choose_number(), will return an integer, as will the false option. So in my mind the type-checking feature of if_else should be satisfied.

1 Answers

the reason is because dplyr::if_else() is more strict than ifelse().

dplyr::if_else() needs to check that both true and false are the same type and class before it is able to execute. since choose_number() is a function, it needs to be resolved first for this class check to take place, so it runs even when your condition is FALSE.

more info here: https://rdrr.io/cran/dplyr/man/if_else.html

Related