Suharto Anggono Suharto Anggono
2019-Apr-03 02:40 UTC
[Rd] stopifnot -- eval(*) inside for()
With f <- function(x) for (i in 1) x fc <- cmpfun(f) (my previous example), error message of fc(is.numeric(y)) shows the originating call as well, while error message of f(is.numeric(y)) doesn't. Compiled version behaves differently. Even with f <- function(x) for (i in 1) {x; eval(expression(i))} fc <- cmpfun(f) , error message of fc(is.numeric(y)) shows the originating call in R 3.3.1. As I see, error message only has one line of call. If the deparsed call spans more than one line, the rest is not shown. In 'stopifnot' in R 3.5.x, each is wrapped in 'tryCatch' which is wrapped again in 'withCallingHandlers'. Just one wrapping may be enough. The 'withCallingHandlers' construct in 'stopifnot' in R 3.5.x has no effect anyway, as I said before (stat.ethz.ch/pipermail/r-devel/2019-February/077386.html). Also, 'tryCatch' (or 'withCallingHandlers' ...) can wrap the entire 'for' loop. The slowdown can be less than in R 3.5.x. -------------------------------------------- On Mon, 1/4/19, Martin Maechler <maechler at stat.math.ethz.ch> wrote: Subject: Re: [Rd] stopifnot -- eval(*) inside for() Cc: r-devel at r-project.org Date: Monday, 1 April, 2019, 5:00 PM>>>>> Suharto Anggono Suharto Anggono via R-devel >>>>> on Sun, 31 Mar 2019 15:26:13 +0000 writes:> Ah, with R 3.5.0 or R 3.4.2, but not with R 3.3.1, 'eval' > inside 'for' makes compiled version behave like > non-compiled version. Ah.. ... thank you for detecting that " eval() inside for()" behaves specially in how error message get a call or not. Let's focus only on this issue here. I'm adding a 0-th case to make even clearer what you are saying: > options(error = expression(NULL)) > library(compiler) > enableJIT(0) > f0 <- function(x) { x ; x^2 } ; f0(is.numeric(y)) Error in f0(is.numeric(y)) (from #1) : object 'y' not found > (function(x) { x ; x^2 })(is.numeric(y)) Error in (function(x) { (from #1) : object 'y' not found > f0c <- cmpfun(f0) ; f0c(is.numeric(y)) so by default, not only the error message but the originating call is shown as well. However, here's your revealing examples: > f <- function(x) for (i in 1) {x; eval(expression(i))} > f(is.numeric(y)) > # Error: object 'y' not found > fc <- cmpfun(f) > fc(is.numeric(y)) > # Error: object 'y' not found I've tried more examples and did not find any difference between simple interpreted and bytecompiled code {apart from "keep.source=TRUE" keeping source, sometimes visible}. So I don't understand yet why you think the byte compiler plays a role. Rather the crucial difference seems the error happens inside a loop which contains an explicit eval(.), and that eval() may even be entirely unrelated to the statement in which the error happens [above: The error happens when the promise 'x' is evaluated, *before* eval() is called at all]. > Is this accidental feature going to be relied upon? [i.e. *in stopifnot() R code (which in R-devel and R 3.5.x has had an eval() inside the for()-loop)] That is a good question. What I really like about the R-devel case: We do get errors signalled that do *not* contain the full stopifnot() call. With the newish introduction of the `exprs = { ... ... }` variant, it is even more natural to have large `exprs` in a stopifnot() call, and when there's one accidental error in there, it's quite unhelpful to see the full stopifnot(..........) call {many lines of R code} obfuscating the one statement which produced the error. So it seems I am asking for a new feature in R, namely to temporarily say: Set the call to errors to NULL "in the following". In R 3.5.x, I had used withCallingHandlers(...) to achieve that and do even similar for warnings... but needed to that for every expression and hence inside the for loop and the consequence was a relatively large slowdown of stopifnot().. which triggered all the changes since. Whereas what we see here ["eval() inside for()"] is a cheap automatic suppression of 'call' for the "internal errors", i.e., those we don't trigger ourselves via stop(simplError(...)).