Is there any way to programmatically tell if a given function in r has standard evaluation, and if not, which component of function evaluation –
parsing,
matching,
scoping,
promise formation,
promise fulfillment,
return,
etc. – is non-standard? I understand that closures are likely to be standard, and primitives are likely to be non-standard, but there are exceptions both ways. I’m asking about determining whether the function semantics are standard with respect to each of these things, not whether the function mechanics are standard.
I assume these things ought to be derivable from a close and careful reading of the help page, and failing that the code, and failing that any referenced source code. But it would save me a great deal of grief if I had a mechanical way of quickly identifying non-standard features in the evaluation of a given function.
If there is not a way to programmatically identify all the ways in which a function is nonstandard, are there ways to test for any aspect of standardness?
Quick way to check for non-standard evaluation (NSE) is to verify if certain keywords are used, e.g. substitute, eval, deparse etc.
Please see the code below. It looks into the body of function and count how many times NSE-related keywords are used.
is_nse <- function(x) {
nse_criteria <- c("substitute", "deparse", "eval", "parent.frame", "quote")
code <- as.character(body(x))
print(x)
cat("-------------------------------------------------------------------\n")
nse_count <- sapply(nse_criteria, function(x) sum(grepl(x, code)))
if(sum(nse_count) > 0)
warning("Possible non-standard evaluation")
nse_count
}
is_nse(as.Date.default)
Output:
function (x, ...)
{
if (inherits(x, "Date"))
x
else if (is.null(x))
.Date(numeric())
else if (is.logical(x) && all(is.na(x)))
.Date(as.numeric(x))
else stop(gettextf("do not know how to convert '%s' to class %s",
deparse1(substitute(x)), dQuote("Date")), domain = NA)
}
<bytecode: 0x0000021be90e8f18>
<environment: namespace:base>
-------------------------------------------------------------------
substitute deparse eval parent.frame quote
1 1 0 0 0
Warning message:
In is_nse(as.Date.default) : Possible non-standard evaluation
Related
lists and environments support the dollar operator in R, so I can do lst$whatever and env$whatever. Other entities, like atomic vectors, do not, for example I can't do vctr$whatever.
Is there a way to programmatically know if a passed entity supports the dollar operator?
having names() apparently is not good, because vectors can have names but are still not dollar indexable. ls() may seem a good approach but it requires that the entity can be converted to an environment, which may not always be the case.
There's no method that will tell you for sure if something will respond to the $ function. But even it there was, there's no guarantee what the $ would do. The $ is generic and classes are free to redefine how it behaves. For example, it could be used to draw a plot
foo <- function(x) {
structure(x, class="foo")
}
`$.foo`<-function(x, v, ...) {
plot(seq.int(nchar(v)), seq.int(nchar(v)), main=v)
}
x <- foo(5)
x$hello
So just because it will respond to $ doesn't mean it will actually return/extract a value.
If you expect $ to have a certain behavior, then you should test for classes that actually have that behavior. If you want to just try to use $, you can always just catch the error in a tryCatch. Here we just return NULL when it fails but you could return whatever you like.
tryCatch(thing$whatever, error=function(e) NULL)
I came across this spinet of code where the function rval_top_ingredients() was used to render a D3wordcloud before it was defined. I think that would throw an error in case of Python as the script is executed from top to bottom. Why did it work in R then? Thankyou.
output$wc_ingredients <- d3wordcloud::renderD3wordcloud({
ingredients_df <- rval_top_ingredients()
d3wordcloud(ingredients_df$ingredient, ingredients_df$nb_recipes, tooltip = TRUE)
})
rval_top_ingredients <- reactive({
recipes_enriched %>%
filter(cuisine == input$cuisine) %>%
arrange(desc(tf_idf)) %>%
head(input$nb_ingredients) %>%
mutate(ingredient = forcats::fct_reorder(ingredient, tf_idf))
})
R doesn’t differ from Python here: you can’t use a function before it’s defined. But, despite appearances to the contrary, this also isn’t happening here.
d3wordcloud::renderD3wordcloud is a special function call which doesn’t evaluate its arguments immediately. In fact, the argument is stored internally as an unevaluated expression and is only evaluated later after a certain trigger. By that time, rval_top_ingredients has been defined.
This is a pervasive pattern in Shiny, but you can harness this behaviour yourself. Consider the following:
f = function (expr) {}
f(g())
g = function () { stop('oh no!') }
This code works, since f never uses its argument, and since R uses lazy evaluation for function arguments: unlike most other languages, a function argument only gets evaluated once it is used. Arguments that are never used are never evaluated.
So, despite the fact that f(g()) appears to use g before it’s defined, the actual call to f never evaluates its arguments so there’s no issue. The only constraint is that the argument needs to be syntactically valid.
Here’s a slightly more meaningful example which does something useful (it creates a function that creates a log message before evaluating an expression:
make_verbose = function (expr) {
function () {
message(sprintf('Evaluating %s', deparse(substitute(expr))))
expr
}
}
verbose_g = make_verbose(g())
g = function () {
message('g was called!')
}
verbose_g()
Python doesn’t quite support this, since Python doesn’t have lazy and non-standard evaluation. But a similar situation still exists in Python:
def f():
g()
def g():
print('g()')
f()
Here, g() is seemingly used before it was defined; but this is only true if we’re reading the code textually from top top bottom without paying attention to scope. In reality, g() is only ever called after it was defined. The same is true in the R code you’ve posted.
I created a function to convert a function name to string. Version 1 func_to_string1 works well, but version 2 func_to_string2 doesn't work.
func_to_string1 <- function(fun){
print(rlang::as_string(rlang::enexpr(fun)))
}
func_to_string2 <- function(fun){
is.function(fun)
print(rlang::as_string(rlang::enexpr(fun)))
}
func_to_string1 works:
> func_to_string1(sum)
[1] "sum"
func_to_string2 doesn't work.
> func_to_string2(sum)
Error: Can't convert a primitive function to a string
Call `rlang::last_error()` to see a backtrace
My guess is that by calling the fun before converting it to a string, it gets evaluated inside function and hence throw the error message. But why does this happen since I didn't do any assignments?
My questions are why does it happen and is there a better way to convert function name to string?
Any help is appreciated, thanks!
This isn't a complete answer, but I don't think it fits in a comment.
R has a mechanism called pass-by-promise,
whereby a function's formal arguments are lazy objects (promises) that only get evaluated when they are used.
Even if you didn't perform any assignment,
the call to is.function uses the argument,
so the promise is "replaced" by the result of evaluating it.
Nevertheless, in my opinion, this seems like an inconsistency in rlang*,
especially given cory's answer,
which implies that R can still find the promise object even after a given parameter has been used;
the mechanism to do so might not be part of R's public API though.
*EDIT: see coments.
Regardless, you could treat enexpr/enquo/ensym like base::missing,
in the sense that you should only use them with parameters you haven't used at all in the function's body.
Maybe use this instead?
func_to_string2 <- function(fun){
is.function(fun)
deparse(substitute(fun))
#print(rlang::as_string(rlang::enexpr(fun)))
}
> func_to_string2(sum)
[1] "sum"
This question brings up an interesting point on lazy evaluations.
R arguments are lazily evaluated, meaning the arguments are not evaluated until its required.
This is best understood in the Advanced R book which has the following example,
f <- function(x) {
10
}
f(stop("This is an error!"))
the result is 10, which is surprising because x is never called and hence never evaluated. We can force x to be evaluated by using force()
f <- function(x) {
force(x)
10
}
f(stop("This is an error!"))
This behaves as expected. In fact we dont even need force() (Although it is good to be explicit).
f <- function(x) {
x
10
}
f(stop("This is an error!"))
This what is happening with your call here. The function sum which is a symbol initially is being evaluated with no arguments when is.function() is being called. In fact, even this will fail.
func_to_string2 <- function(fun){
fun
print(rlang::as_string(rlang::ensym(fun)))
}
Overall, I think its best to use enexpr() at the very beginning of the function.
Source:
http://adv-r.had.co.nz/Functions.html
consumeSingleRequest <- function(api_key, URL, columnNames, globalParam="", ...)
consumeSingleRequest <- function(api_key, URL, columnNames, valuesList, globalParam="")
I am trying to overload a function like this, that takes in multiple lists in the first function and combines them into one list of lists. However, I don't seem to be able to skip passing in globalParam and pass in oly the multiple lists in the ...
Does anyone know how to do that?
I've heard S3 methods could be used for that? Does anyone know how?
R doesn't support a concept of overloading functions. It supports function calls with variable number of arguments. So you can declare a function with any number of arguments, but supply only a subset of those when actually calling a function. Take vector function as an example:
> vector
function (mode = "logical", length = 0L)
.Internal(vector(mode, length))
<bytecode: 0x103b89070>
<environment: namespace:base>
It supports up to 2 parameters, but can be called with none or some subset(in that case default values are used) :
> vector()
logical(0)
> vector(mode='numeric')
numeric(0)
So you only need a second declaration:
consumeSingleRequest <- function(api_key, URL, columnNames, valuesList, globalParam="")
And supply just supply the needed parameters when actually calling the function
consumeSingleRequest(api_key=..., valueList=...)
P.S. A good explanation can be found in Advanced R Book.
I would like to find all functions in a package that use a function. By functionB "using" functionA I mean that there exists a set of parameters such that functionA is called when functionB is given those parameters.
Also, it would be nice to be able to control the level at which the results are reported. For example, if I have the following:
outer_fn <- function(a,b,c) {
inner_fn <- function(a,b) {
my_arg <- function(a) {
a^2
}
my_arg(a)
}
inner_fn(a,b)
}
I might or might not care to have inner_fn reported. Probably in most cases not, but I think this might be difficult to do.
Can someone give me some direction on this?
Thanks
A small step to find uses of functions is to find where the function name is used. Here's a small example of how to do that:
findRefs <- function(pkg, fn) {
ns <- getNamespace(pkg)
found <- vapply(ls(ns, all.names=TRUE), function(n) {
f <- get(n, ns)
is.function(f) && fn %in% all.names(body(f))
}, logical(1))
names(found[found])
}
findRefs('stats', 'lm.fit')
#[1] "add1.lm" "aov" "drop1.lm" "lm" "promax"
...To go further you'd need to analyze the body to ensure it is a function call or the FUN argument to an apply-like function or the f argument to Map etc... - so in the general case, it is nearly impossible to find all legal references...
Then you should really also check that getting the name from that function's environment returns the same function you are looking for (it might use a different function with the same name)... This would actually handle your "inner function" case.
(Upgraded from a comment.) There is a very nice foodweb function in Mark Bravington's mvbutils package with a lot of this capability, including graphical representations of the resulting call graphs. This blog post gives a brief description.