Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check whether certain function is used inside a nested function in R

Tags:

function

r

I want to create a function check_sym that takes another function f as argument and checks whether inside f a specific function is used.

Specifically I want to check if inside f non-standard evaluation in the form of !! sym is used.

I can do this with a simple trick that turns a functions body into a character string and then uses regex to check for "!!sym\\(".

library(dplyr)
library(purrr)
library(rlang)

check_sym <- function(f) {
  f <- as.list(body(f))
  f <- unlist(as.character(f))
  purrr::some(f, function(x) grepl("!!sym\\(", x))
}

foo <- function(df, x) { 
  select(df, !! sym(x))
}

check_sym(foo)
#> [1] TRUE

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

However, while this is possible, I am looking for a way that does not rely on character strings and regex, but rather ideally some method which looks inside the function and "sees" all function calls at a deeper level, which would be more reliable.

Any ideas appreciated.

Final solution based on accepted answer:

Based on MrFlick's answer below my actual solution is the following:

I define check_syms as:

check_sym <- function(f) {
  grepl("!!sym", paste(all.names(body(f)), collapse = ""))
}

It correctly identifies functions which make a function call to "!! sym" compared to functions that only call for example paste0("!!sym").

foo <- function(df, x) { 
  select(df, !! sym(x))
}

test_f <- function(x) {
  print(paste0("!!sym", x))
}

check_sym(foo)
#> [1] TRUE

check_sym(test_f)
#> [1] FALSE
like image 459
TimTeaFan Avatar asked Feb 16 '20 22:02

TimTeaFan


Video Answer


2 Answers

In base R, we can capture the output of body into a vector of strings and use grepl

check_sym <- function(f)  any(grepl('!!sym(', capture.output(body(f)), fixed = TRUE))

check_sym(foo)
# [1] TRUE 


some_fct <- function(x) print("sym")
check_sym(some_fct)
#[1] FALSE
like image 156
akrun Avatar answered Oct 07 '22 10:10

akrun


You could use the all.names function. It pulls out the names of all the variables/function defined inside the function

all.names(body(foo))
# [1] "{"      "select" "df"     "!"      "!"      "sym"   
# [7] "x"

Part of the problem with your specific example is that !!sym isn't really a simple variable. It's more like a call to !(!(sym)) to the parser. The !! stuff really isn't a special operator to the R parser, it's just two calls to the negation operator which rlang re-interprets via non-standard evaluation.

like image 22
MrFlick Avatar answered Oct 07 '22 10:10

MrFlick