How to test if a variable holds a lambda-function?

Viewed 81

I'm building a function where the main argument can be various things, e.g. a formula, a function or a lambda-function and it has to handle things accordingly.

library(rlang)
my_func=function(x){
    if(is_function(x))
        return("X is a function")
    else if(is_lambda(x))
        return("X is a lambda")
    else if(is_formula(x))
        return("X is a formula")
    else 
        return("X is something else")
}
my_func(x=is.numeric) #function
my_func(x=A~B) #formula
my_func(x=~is.numeric(.x)) #formula too :'(
my_func(x="foo") #something else

As you can see, rlang::is_lambda did not recognize the lambda-function. In the help file example, they use as_function first (is_lambda(as_function(x))), but this throws a conversion error when x is not a lambda-function-formula. I'd rather not use trycatch if possible, as I find it sometimes leads to unreadable code and hidden bugs.

How can I test this case simply?

2 Answers

You could deparse the formula and test whether its first character is a tilde. This will mean any formula without a left hand side that could be coerced to a lambda will be recognised as such, but I'm guessing that is the desired behaviour anyway.

library(rlang)

my_func=function(x){
    if(is_function(x))
        return("X is a function")
    else if(is_formula(x)){
        if(substr(deparse(x), 1, 1) ==  "~"){
            if(is_lambda(as_function(x))) 
                return("X is a lambda")
        }
        return("X is a formula")
    }
    else 
        return("X is something else")
}

my_func(x=is.numeric)
#> [1] "X is a function"
my_func(x=A~B) 
#> [1] "X is a formula"
my_func(x=~is.numeric(.x)) 
#> [1] "X is a lambda"
my_func(x="foo") 
#> [1] "X is something else"

Created on 2020-03-16 by the reprex package (v0.3.0)

While Allan's answer works fine, I decided to stick to what rlang was doing.

If you take a look at View(rlang::as_function), you'll see that it aborts only if (length(x) > 2).

Therefore, my code can be written as:

library(rlang)
my_func=function(x){
  if(is_function(x))
    return("X is a function")
  else if(is_formula(x)){
    if(length(x) > 2)
      return("X is a formula")
    else if(is_lambda(as_function(x)))
      return("X is a lambda")
  }
  return("X is something else")
}

This will return the expected output every time.

Related