Skip to content

stopifnot -- eval(*) inside for()

3 messages · Martin Maechler, Luke Tierney

#
> 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(...)).
#
On Mon, 1 Apr 2019, Martin Maechler wrote:

            
Don't count on that remaining true indefinitely. The standard behavior
is better and we'll eventually get the case where 'eval' and a few
others are called to behave the same.

Best,

luke

  
    
#

        
> On Mon, 1 Apr 2019, Martin Maechler wrote:
>>>>>>> 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.

    > Don't count on that remaining true indefinitely. The standard behavior
    > is better and we'll eventually get the case where 'eval' and a few
    > others are called to behave the same.

    > Best,
    > luke

Yes, Suharto did indeed mention that it may not be a good idea
to rely on that behavior, and I did agree ... though my
agreement was a bit buried in other stuff.

Note that I have been asking if this "accidental" but internally
consistent behavior for the current situation, could not be made
a feature that the user can ask for ... without having to use a
handler (which had been a real slowdown when used inside
stopifnot() in R 3.5.3) :


  [................]
  [................]

    >> 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(simpleError(...)).

So, for me as programmeR, it would be nice to be able to ask for
"this" behavior explicitly in a special case as here, where
"no-call" error messages are preferable .. because the call can
be really large and is known to be  "almost surely" distracting
rather than helpful.

Martin