R: promise already under evaluation - r

I understand that you are probably sick and tired of answering the same question again, but I am still getting the error discussed in several other questions:
promise already under evaluation: recursive default argument reference or earlier problems?
even though I did follow the "cumbersome" advice of prepending ".":
show.large.objects.threshold <- 100000
show.large.objects.exclude <- c("closure")
show.large.objects <- function (.envir = sys.frame(),
threshold = show.large.objects.threshold,
exclude = show.large.objects.exclude) {
for (n in print(ls(.envir, all.names = TRUE))) tryCatch({
o <- get(n,envir = .envir)
s <- object.size(o)
if (s > threshold && !(typeof(o) %in% exclude)) {
cat(n,": ")
print(s,units="auto")
}
}, error = function(e) { cat("n=",n,"\n"); print(e) })
}
show.large.objects.stack <- function (.threshold = show.large.objects.threshold,
skip.levels = 1,# do not examine the last level - this function
.exclude = show.large.objects.exclude) {
for (level in 1:(sys.nframe()-skip.levels)) {
cat("*** show.large.objects.stack(",level,") ")
print(sys.call(level))
show.large.objects(.envir = sys.frame(level), threshold = .threshold, exclude = .exclude)
}
}
but I still get errors:
> f <- function () { c <- 1:1e7; d <- 1:1e6; print(system.time(show.large.objects.stack())) }
> f()
*** show.large.objects.stack( 1 ) f()
[1] "c" "d"
c : 38.1 Mb
d : 3.8 Mb
*** show.large.objects.stack( 2 ) print(system.time(show.large.objects.stack()))
[1] "..." "x"
n= ...
<simpleError in get(n, envir = .envir): argument "..." is missing, with no default>
n= x
<simpleError in get(n, envir = .envir): promise already under evaluation: recursive default argument reference or earlier problems?>
*** show.large.objects.stack( 3 ) system.time(show.large.objects.stack())
[1] "expr" "gcFirst" "ppt" "time"
n= expr
<simpleError in get(n, envir = .envir): promise already under evaluation: recursive default argument reference or earlier problems?>
user system elapsed
0 (0.00ms) 0 (0.00ms) 0.002 (2.00ms)
So, what am I still doing wrong?
Do I really need the . in .envir? What about .exclude and .threshold?
Why do I get the argument "..." is missing, with no default error?
Why do I get the promise already under evaluation error?
Thanks!

When f is called a stack of 5 levels is built down to show.large.objects, which starts to evaluate the contents of the frames starting from the top.
f
-> print
-> system.time
-> show.large.objects.stack
-> show.large.objects
Level 1
f()
Everything ok here.
Level 2
print(system.time(show.large.objects.stack()))
When you call ls(.envir, all.names) on its frame you get
[1] "..." "x"
of which ... is missing and throws error 3 when you call get on it, and x = system.time(show.large.objects.stack()) is currently being evaluated and throws error 4.
Level 3
system.time(show.large.objects.stack())
whose ls gives you
[1] "expr" "gcFirst" "ppt" "time"
of which expr = show.large.objects.stack() is still currently being evaluated and throws another of error 4.
Level 4
show.large.objects.stack()
whose ls contain no sketchy things and completes without errors.
Bottom line
show.large.frames() must be evalutad on its own, not as an argument to any function, or it will throw errors. Why not letting it do the printing itself?
I found this very helpful
> debug(show.large.objects)
> f()
Browse[2]> lapply(sys.frames(), ls)
[[1]]
[1] "c" "d"
[[2]]
[1] "x"
[[3]]
[1] "expr" "gcFirst" "ppt" "time"
[[4]]
[1] "level" "skip.levels"
[[5]]
[1] "exclude" "threshold"

Related

When debugging in RStudio, how to operate an object in another function in the stack?

I have a basic question with RStudio as following.
When I run the following R code in RStudio, it will pause at browser() in function f3(). At that point, I want to operate on the object i1 in function f1(). For example, I want to print(i1).
However, I found that I cannot do it. Does anyone know how to do it?
f1 <- function() {
i1 <- 1
f2()
}
f2 <- function() {
i2 <- 2
f3()
}
f3 <- function() {
i3 <- 3
browser()
}
f1()
Each level of function call comes with its own environment. You can access them with the parent.frame function.
Browse[1]> ls()
[1] "i3"
Browse[1]> ls(parent.frame())
[1] "i2"
Browse[1]> ls(parent.frame(2))
[1] "i1"
Browse[1]> ls(parent.frame(3))
[1] "f1" "f2" "f3"
The last one above is the global environment.
Then you can get or change a value:
Browse[1]> get("i1", envir = parent.frame(2))
[1] 1
Browse[1]> assign("i1", 10, envir = parent.frame(2))
Browse[1]> get("i1", envir = parent.frame(2))
[1] 10
Here is a function to get the value of a local variable in any parent frame:
getval <- function(name) {
i <- 0
repeat {
i <- i + 1
e <- parent.frame(i)
if (exists(name, envir = e)) return(get(name, envir = e))
if (identical(e, .GlobalEnv)) break
}
}
Browse[1]> getval("i3")
[1] 3
Browse[1]> getval("i2")
[1] 2
Browse[1]> getval("i1")
[1] 10
You may write an equivalent setval function if necessary.
If you only want to look at the values of other functions that have been called in the process of the script you can use the traceback functionality shown here on the bottom right:
If you select f1() there, you will see the value of i1 directly in the Environment view:

Use apply function instead of apply for eval parse instead of loop

It's complicated to explain my use case but I am working on a project that requires parsing text that may throw some errors. I would like to use tryCatch() so that as much of the script can run as possible and alert the user that some code failed. I can use a loop for this but I would like to know why this behaviour exists and if there is an apply function that does do the trick.
When I run the loop or use do.call() on this parsed object, I get just the expected single error mesage. When I use lapply() I get the same error message followed by the ouput of the assignments. I've tried throwing suppress functions around lapply() which, perhaps obviously, did not work. and I get similar output for sapply() and map(). Curious if someone can explain it to me.
test_text <- parse(text = "x <- pi; y <- x; z <- stop()")
eval_try <- function(x) {
tryCatch(
eval(x, envir = .GlobalEnv),
error = function(cond) message("there was an error"),
warning = function(cond) message("there was a warning")
)
}
for (i in seq_along(test_text)) {
eval_try(test_text[i])
}
#> there was an error
do.call("eval_try", list(test_text))
#> there was an error
lapply(test_text, eval_try)
# there was an error
# [[1]]
# [1] 3.141593
#
# [[2]]
# [1] 3.141593
#
# [[3]]
# NULL
The printing you see is the output of the lapply function. You can suppress it by assigning the result to a variable or if you really don't care about storing the output, use the below trick with invisible.
> myfun <- function(x) x
>
> lapply(1:3, FUN = myfun)
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
> a <- lapply(1:3, FUN = myfun)
> invisible(lapply(1:3, FUN = myfun))

implementation of dot function `.()` in data.table package [duplicate]

This question already has an answer here:
How is dot (.) alias for list constructor implemented in data.table package?
(1 answer)
Closed 4 years ago.
from ?data.table::data.table :
The expression '.()' is a shorthand alias to list(); they both mean
the same
However this function is nowhere to be found :
data.table:::.
Error in get(name, envir = asNamespace(pkg), inherits = FALSE) :
object '.' not found
So I suppose the input is parsed somehow, how is it done ? I'd like to use the same feature in my own package.
The following works not too bad :
test <- function(x) {
eval(substitute(
eval.parent(substitute(x, list(.=list)))
))
}
foo <- "bar"
test(.(foo))
# [[1]]
# [1] "bar"
identical(test(.(foo)), list(foo))
# [1] TRUE
However there will be some dot variables used inside this dot function, and this fails :
. <- "baz"
test(.(foo,.))
# [[1]]
# [1] "bar"
#
# [[2]]
# function (...) .Primitive("list")
Expected :
# [[1]]
# [1] "bar"
#
# [[2]]
# [1] "baz"
The data.table package accomplishes it with this bit of code
replace_dot_alias <- function(e) {
# we don't just simply alias .=list because i) list is a primitive (faster to iterate) and ii) we test for use
# of "list" in several places so it saves having to remember to write "." || "list" in those places
if (is.call(e)) {
# . alias also used within bquote, #1912
if (e[[1L]] == 'bquote') return(e)
if (e[[1L]] == ".") e[[1L]] = quote(list)
for (i in seq_along(e)[-1L]) if (!is.null(e[[i]])) e[[i]] = replace_dot_alias(e[[i]])
}
e
}
found in R/data.table.R (currently at line 173). That's why you don't find data.table:::. anywhere, and how they accomplish the parsing you mention in your post.
Then in [.data.table" <- function (x, i, j,... they can do this sort of thing
if (!missing(j)) {
jsub = replace_dot_alias(substitute(j))
root = if (is.call(jsub)) as.character(jsub[[1L]])[1L] else ""
....

Warnings suppressed with mclapply in R

With mclapply() all issued warnings seems get suppressed:
library(multicore)
mclapply(1:3, function(x) warning(x))
[[1]]
[1] "1"
[[2]]
[1] "2"
[[3]]
[1] "3"
while lapply would give:
lapply(1:3, function(x) warning(x))
[[1]]
[1] "1"
[[2]]
[1] "2"
[[3]]
[1] "3"
Warning messages:
1: In FUN(1:3[[3L]], ...) : 1
2: In FUN(1:3[[3L]], ...) : 2
3: In FUN(1:3[[3L]], ...) : 3
Any tips on how to avoid loosing the warnings?
According to mclapply's help page, in my opinion the argument mc.silent should allow you to chose if warnings are to be printed or not. Strangely, it does not do that. Setting it explictly to TRUE or FALSE does not have any effect in your situation.
So that leaves us only with a somewhat dirty hack: forcing R to print warnings as they occur.
options(warn=1)
mclapply(1:3, function(x) warning(x))
# Warning in FUN(1L[[1L]], ...) : 1
# Warning in FUN(2L[[1L]], ...) : 2
# Warning in FUN(3L[[1L]], ...) : 3
# [[1]]
# [1] "1"
#
# [[2]]
# [1] "2"
#
# [[3]]
# [1] "3"
I have this problem too. If I'm reading the code correctly, parallel::mclapply() passes the mc.silent option to parallel:mcparallel(). But mcparallel() has this line:
sendMaster(try(eval(expr, env), silent = TRUE))
I think that's the place where the warnings would be sent back to the master process, but the mc.silent is not respected. That's my best guess about what is going on.
For anyone who will come around the same issue, here is a workaround:
safe_mclapply <- function(X, FUN, mc.cores, stop.on.error=T, ...){
fun <- function(x){
res_inner <- tryCatch({
withCallingHandlers(
expr = {
FUN(x, ...)
},
warning = function(e) {
message_parallel(trimws(paste0("WARNING [element ", x,"]: ", e)))
# this line is required to continue FUN execution after the warning
invokeRestart("muffleWarning")
},
error = function(e) {
message_parallel(trimws(paste0("ERROR [element ", x,"]: ", e)))
}
)},
error = function(e){
# error is returned gracefully; other results of this core won't be affected
return(e)
}
)
return(res_inner)
}
res <- mclapply(X, fun, mc.cores=mc.cores)
failed <- sapply(res, inherits, what = "error")
if (any(failed == T)){
error_indices <- paste0(which(failed == T), collapse=", ")
error_traces <- paste0(lapply(res[which(failed == T)], function(x) x$message), collapse="\n\n")
error_message <- sprintf("Elements with following indices failed with an error: %s. Error messages: \n\n%s",
error_indices,
error_traces)
if (stop.on.error)
stop(error_message)
else
warning(error_message, "\n\n### Errors will be ignored ###")
}
return(res[!failed])
}
#' Function which prints a message using shell echo; useful for printing messages from inside mclapply when running in Rstudio
message_parallel <- function(...){
system(sprintf('echo "\n%s\n"', paste0(..., collapse="")))
}
safe_mclapply above is a wrapper around mclapply. For each iteration it uses withCallingHandlers to catch and print warnings and errors; note that invokeRestart("muffleWarning") is required in order to continue FUN exection and return the result. Printing is done via message_parallel function which uses shell echo to print messages to R console (tested to work in Rstudio).
safe_mclapply provides few more features which you might find optional:
along with the warning and error it prints character representation of x which I find useful because it gives an idea where the message comes from
tryCatch around withCallingHandlers helps to return an error gracefully so that other results of the core are not affected
after mclapply is executed, the indices of error results are printed
stop.on.error provides an option to ignore any results which contain error and continue despite the errors
Side note: I personally prefer pbmclapply function from pbmcapply over mclapply which adds a progress bar. You can change mclapply to pbmclapply in the code above.
Small snippet to test the code:
X <- list(1, 2, 3, 4, 5, 6)
f <- function(x){
if (x == 3){
warning("a warning")
warning("second warning")
}
if (x == 6){
stop("an error")
}
return(x + 1)
}
res <- safe_mclapply(X = X, FUN = f, mc.cores=16)
res_no_stop <- safe_mclapply(X = X, FUN = f, mc.cores=16, stop.on.error = F)

How do I query for values of symbols in a closure in R?

How can I query the value of x for foo in the R code below?
make.foo <- function() {
x <- 123
function() x * 3
}
foo <- make.foo()
# now get foo's x
A function will have an environment
from ?`function`
A closure has three components, its formals (its argument list), its body (expr in the ‘Usage’ section) and its environment which provides the enclosure of the evaluation frame when the closure is used.
so you can get from that environment (or list the objects using ls)
get('x', envir = environment(foo))
## [1] 123
or if you want to know all the objects in the environment
ls(envir = environment(foo))
## 'x'
and if you want to assign to that environment (ie change x)
assign('x', 24, envir = environment(foo))
foo()
## 72
You can even remove it from the environment
rm(x, envir = environment(foo))
foo()
## Error in foo() : object 'x' not found
and then use a globally assigned x
x <- 3
foo()
# [1] 9
and reassign to the function's environment
assign('x', 123, envir = environment(foo))
foo()
## [1] 369
If you want to look for something in an object's environment and nowhere else then use get with inherits=FALSE. Otherwise you'll risk finding things in the function's parent environment. Example using your make.foo above:
> z=999
> get("x",environment(foo))
[1] 123
> get("z",environment(foo))
[1] 999
> get("x",environment(foo),inherits=FALSE)
[1] 123
> get("z",environment(foo),inherits=FALSE)
Error in get("z", environment(foo), inherits = FALSE) :
object 'z' not found
The second get shows that you might not get an error if you try and get something that isn't in the closure's environment if it appears in the parent environment. This may cause odd bugs. With inherits=FALSE you get an immediate error.

Resources