[Rd] stopifnot -- eval(*) inside for()

Martin Maechler m@ech|er @end|ng |rom @t@t@m@th@ethz@ch
Mon Apr 1 12:00:02 CEST 2019


>>>>> 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