CoderGuy123
CoderGuy123

Reputation: 6669

all.equal with a list and quote(expr = )

Some time ago I made a function that checks whether an argument is missing and if it is, it returns an informative error. It looks like this:

#' Check if arguments are missing and raise error if they are
#'
#' Checks if arguments are missing and raises an error if they are. Put this in the beginning of your functions to check the input and give useful errors without writing checking code over and over again.
#' @param var_names (chr vector) Names of variables to check.
#' @param error_msg (chr scalar) A template of the error message to show.
#' @export
#' @examples
#' test_func = function(y) {
#' check_missing("y")
#' T
#' }
#' test_func(y = ) #throws error
#' test_func(y = 1) #returns true
check_missing = function(var_names, error_msg = "[VAR] was missing! Please supply the input and try again.") {

  #parent.frame as list
  pf = as.list(parent.frame())

  #check each if missing
  for (name in var_names) {
    #is it there at all?
    if (!name %in% names(pf)) {
      stop(name + " is not even found in the parent.frame! Check the variable names.", call. = F)
    }

    #check if missing
    if (are_equal(pf[[name]], quote(expr = ))) {
      stop(str_replace(error_msg, pattern = "\\[VAR\\]", name), call. = F)
    }
  }

  #all fine
  return(invisible(NULL))
}

Function from this package, use with:

library(devtools);install_github("deleetdk/kirkegaard");library("kirkegaard")

In a particular use case, I get a list as an argument passed to the checker function above. However, this results in:

Error in all.equal.list(target, current, ...) : 
  argument "current" is missing, with no default

One can reproduce this with this minimal example:

> all.equal(list(), quote(expr = ))
Error in all.equal.list(list(), quote(expr = )) : 
  argument "current" is missing, with no default

Is there a bug in the base-r function or am I misunderstanding something?

I hacked around the problem by inserting:

if (is.list(pf[[name]])) return(invisible(NULL)) #evade bug?

before the offending line (the call to are_equal, my wrapper for all.equal).

Upvotes: 0

Views: 125

Answers (1)

Lars Arne Jordanger
Lars Arne Jordanger

Reputation: 671

The problem should disappear if you use identical instead of all.equal.

First of all, note that quote(expr = ) creates a missing object:

test_env <- new.env()
test_env$a <- quote(expr = )

ls.str(test_env)
a : <missing>

Next up, note that all.equal works fine when both of its arguments are missing, but fails when only one of them is missing.

all.equal(target = quote(expr = ), current = test_env$a)
[1] TRUE

all.equal(target = list(), current = test_env$a)
Error in all.equal.list(target = list(), current = test_env$a) : 
  argument "current" is missing, with no default  

The reason for this is that all.equal first checks if the arguments are identical - and if not, it tries to do some further kind of comparison (see ?all.equal for the details).

This problem is not present if you use identical instead of all.equal:

identical(x = quote(expr = ), y = test_env$a)
[1] TRUE

identical(x = list(), y = test_env$a)
[1] FALSE

Based on this, I guess your code should work if you replace (are_equal(pf[[name]], quote(expr = ))) with (identical(pf[[name]], quote(expr = ))).

Upvotes: 1

Related Questions