2

I would like to build a function such as:

test <- function( fct = "default" ) {
  # here test that fct is a string
  # ...
}

in which it is tested that fct is a string of length one. Defining isSingleString() as:

isSingleString <- function(fct) { is.character(fct) & length(fct) == 1 }

seemed like a good idea. Thus:

test <- function( fct = "default" ) {
    # here test that fct is a string
    if (!isSingleString(fct)) stop ("error: not a string")
    # ...
    print("things are running smoothly!")
}

Using test("mean") and test("CI") works ok; test(mean) and and test(2) return the error as expected, but test(CI) crashes, saying that Error: object 'CI' not found.

What can I do to test the argument fct properly?

8
  • 1
    Without the quotes "", CI is treated as a symbol e.g. an object in your environment. If CI does not exist, you will get an error. By example, running CI <- 2; test(CI) and CI <- "CI"; test(CI) works. So for your function, do you want it to treat anything inside test() as literal? Commented Aug 13 at 1:28
  • 1
    I agree with LTyrone, it looks as if you are either (1) trying to operate in NSE using CI as a lazy-typing of "CI", I suggest that is rife with corner-cases and problems that are often not worth it in the end; or (2) a clear misunderstanding on our part for your intent to check not just the class and length of the argument, but if it exists at all. I think Edward's suggestion for tryCatch is appropriate in that second case, but frankly I think that level of resilience (a user supplying a non-existent symbol/name) may be over-engineering things. Commented Aug 13 at 4:07
  • 1
    "What can I do to test the argument fct properly?" Your solution tests the argument properly (although I would use stopifnot and, as mentioned already, &&). You may want to consider if NA_character_ is valid input. Why would you want to catch an object-not-found error only to throw a different error message? That error is very clear. Commented Aug 13 at 5:30
  • 3
    Rather than re-inventing the wheel, consider using the checkmate package. See here for details. In your case, something like assert_character(fct, len = 1). Commented Aug 13 at 5:50
  • 2
    What's not clear about object 'CI' not found? Commented Aug 13 at 15:59

2 Answers 2

2

You probably need to use tryCatch. See this post for some very good explanations on how to use this function.

isSingleString <- function(x) {

  tryCatch(
    {
      # Try to run this command
      is.character(x) && length(x) == 1 
    },
    error = function(e) {
      # Code to execute if an error occurs
      if (grepl("not found", e$message)) {
        message("Error: 'fct' was not found. Please ensure it is defined.")
        return(FALSE)
      } else {
        message("Error: There was an unexpected error.")
        return(FALSE)
      }
    },
    warning = function(w) {
      message("Warning: ", w$message)
    }
  )
}

> test(CI)
Error: 'fct' was not found. Please ensure it is defined.
[1] "error: not a string"
Sign up to request clarification or add additional context in comments.

Comments

2

Expanding on my comment...

library(checkmate)

test <- function(fct) {
  if (test_character(fct, len = 1)) {
    # Do something useful
    print("All OK")
  } else {
    print("Something's wrong")
  }
}
test("ok")
[1] "All OK"
test(c("bad", "worse"))
[1] "Something's wrong"
test(bad)
Error: object 'bad' not found

test(mean)
[1] "Something's wrong"
test(2)
[1] "Something's wrong"

Or, if you want to throw an error rather than print a message...

test1 <- function(fct) {
  assert_character(fct, len = 1)
  print("All OK")
}
test1("ok")
[1] "All OK"
test1(c("bad", "worse"))
Error in test1(c("bad", "worse")) : 
  Assertion on 'fct' failed: Must have length 1, but has length 2.

test1(bad)
Error: object 'bad' not found

test1(mean)
Error in test1(mean) : 
  Assertion on 'fct' failed: Must be of type 'character', not 'closure'.

test1(2)
Error in test1(2) : 
  Assertion on 'fct' failed: Must be of type 'character', not 'double'.

I agree with other commenters that attempting to handle a non-existent symbol passed to the function is probably not worthwhile. Throwing an error in that case seems completely appropriate to me: even if you handle the problem gracefully in test, the non-existent symbol may well cause other problems downstream.

1 Comment

I did not know about checkmate. What a wonderful and useful library!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.