[Rd] stopifnot -- eval(*) inside for()
Suharto Anggono Suharto Anggono
@uh@rto_@nggono @end|ng |rom y@hoo@com
Wed Apr 3 04:40:24 CEST 2019
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 (https://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 using stat.math.ethz.ch> wrote:
Subject: Re: [Rd] stopifnot -- eval(*) inside for()
Cc: r-devel using 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(...)).
More information about the R-devel
mailing list