[Rd] stopifnot() does not stop at first non-TRUE argument

Martin Maechler maechler at stat.math.ethz.ch
Mon May 15 15:37:34 CEST 2017


>>>>> Serguei Sokol <sokol at insa-toulouse.fr>
>>>>>     on Mon, 15 May 2017 13:14:34 +0200 writes:

    > I see in the archives that the attachment cannot pass.
    > So, here is the code:

    [....... MM: I needed to reformat etc to match closely to
     the current source code which is in
     	 https://svn.r-project.org/R/trunk/src/library/base/R/stop.R
     or its corresponding github mirror
        https://github.com/wch/r-source/blob/trunk/src/library/base/R/stop.R
    ]

    > Best,
    > Serguei.

Yes, something like that seems even simpler than Peter's
suggestion...

It currently breaks 'make check' in the R sources,
specifically in tests/reg-tests-2.R (lines 6574 ff),
the new code now gives

  > ## error messages from (C-level) evalList
  > tst <- function(y) { stopifnot(is.numeric(y)); y+ 1 }
  > try(tst())
  Error in eval(cl.i, pfr) : argument "y" is missing, with no default

whereas previously it gave

  Error in stopifnot(is.numeric(y)) : 
     argument "y" is missing, with no default


But I think that change (of call stack in such an error case) is
unavoidable and not a big problem.

--

I'm still curious about Hervé's idea on using  switch()  for the
issue.

Martin


    > Le 15/05/2017 à 12:48, Serguei Sokol a écrit :
    >> Hello,
    >> 
    >> I am a new on this list, so I introduce myself very briefly:
    >> my background is applied mathematics, more precisely scientific calculus
    >> applied for modeling metabolic systems, I am author/maintainer of
    >> few packages (Deriv, rmumps, arrApply).
    >> 
    >> Now, on the subject of this discussion, I must say that I don't really understand
    >> Peter's argument:
    >> 
    >> >>> To do it differently, you would have to do something like
    >> >>>
    >> >>> dots <- match.call(expand.dots=FALSE)$...
    >> >>>
    >> >>> and then explicitly evaluate each argument in turn in the caller
    >> >>> frame. This amount of nonstandard evaluation sounds like it would
    >> >>> incur a performance penalty, which could be undesirable.
    >> The first line of the current stopifnot()
    >> n <- length(ll <- list(...))
    >> already evaluates _all_ of the arguments
    >> in the caller frame. So to do the same only
    >> on a part of them (till the first FALSE or NA occurs)
    >> cannot be more penalizing than the current version, right?
    >> 
    >> I attach here a slightly modified version called stopifnot_new()
    >> which works in accordance with the man page and
    >> where there are only two additional calls: parent.frame() and eval().
    >> I don't think it can be considered as real performance penalty
    >> as the same or bigger amount of (implicit) evaluations was
    >> already done in the current version:
    >> 
    >>> source("stopifnot_new.R")
    >>> stopifnot_new(3 == 5, as.integer(2^32), a <- 12)
    >> Error: 3 == 5 is not TRUE
    >>> a
    >> Error: object 'a' not found
    >> 
    >> Best,
    >> Serguei.
    >> 
    >> 
    >> Le 15/05/2017 à 10:39, Martin Maechler a écrit :
    >>>>>>>> Hervé Pagès <hpages at fredhutch.org>
    >>>>>>>> on Wed, 3 May 2017 12:08:26 -0700 writes:
    >>> > On 05/03/2017 12:04 PM, Hervé Pagès wrote:
    >>> >> Not sure why the performance penalty of nonstandard evaluation would
    >>> >> be more of a concern here than for something like switch().
    >>> 
    >>> > which is actually a primitive. So it seems that there is at least
    >>> > another way to go than 'dots <- match.call(expand.dots=FALSE)$...'
    >>> 
    >>> > Thanks, H.
    >>> 
    >>> >>
    >>> >> If that can't/won't be fixed, what about fixing the man page so it's
    >>> >> in sync with the current behavior?
    >>> >>
    >>> >> Thanks, H.
    >>> 
    >>> Being back from vacations,...
    >>> I agree that something should be done here, if not to the code than at
    >>> least to the man page.
    >>> 
    >>> For now, I'd like to look a bit longer into a possible change to the function.
    >>> Peter mentioned a NSE way to fix the problem and you mentioned switch().
    >>> 
    >>> Originally, stopifnot() was only a few lines of code and meant to be
    >>> "self-explaining" by just reading its definition, and I really would like
    >>> to not walk too much away from that original idea.
    >>> How did you (Herve) think to use  switch()  here?
    >>> 
    >>> 
    >>> 
    >>> >> On 05/03/2017 02:26 AM, peter dalgaard wrote:
    >>> >>> The first line of stopifnot is
    >>> >>>
    >>> >>> n <- length(ll <- list(...))
    >>> >>>
    >>> >>> which takes ALL arguments and forms a list of them. This implies
    >>> >>> evaluation, so explains the effect that you see.
    >>> >>>
    >>> >>> To do it differently, you would have to do something like
    >>> >>>
    >>> >>> dots <- match.call(expand.dots=FALSE)$...
    >>> >>>
    >>> >>> and then explicitly evaluate each argument in turn in the caller
    >>> >>> frame. This amount of nonstandard evaluation sounds like it would
    >>> >>> incur a performance penalty, which could be undesirable.
    >>> >>>
    >>> >>> If you want to enforce the order of evaluation, there is always
    >>> >>>
    >>> >>> stopifnot(A) stopifnot(B)
    >>> >>>
    >>> >>> -pd
    >>> >>>
    >>> >>>> On 3 May 2017, at 02:50 , Hervé Pagès <hpages at fredhutch.org>
    >>> >>>> wrote:
    >>> >>>>
    >>> >>>> Hi,
    >>> >>>>
    >>> >>>> It's surprising that stopifnot() keeps evaluating its arguments
    >>> >>>> after it reaches the first one that is not TRUE:
    >>> >>>>
    >>> >>>> > stopifnot(3 == 5, as.integer(2^32), a <- 12) Error: 3 == 5 is
    >>> >>>> not TRUE In addition: Warning message: In stopifnot(3 == 5,
    >>> >>>> as.integer(2^32), a <- 12) : NAs introduced by coercion to integer
    >>> >>>> range > a [1] 12
    >>> >>>>
    >>> >>>> The details section in its man page actually suggests that it
    >>> >>>> should stop at the first non-TRUE argument:
    >>> >>>>
    >>> >>>> ‘stopifnot(A, B)’ is conceptually equivalent to
    >>> >>>>
    >>> >>>> { if(any(is.na(A)) || !all(A)) stop(...); if(any(is.na(B)) ||
    >>> >>>> !all(B)) stop(...) }
    >>> >>>>
    >>> >>>> Best, H.
    >>> >>>>
    >>> >>>> --
    >>> >>>> Hervé Pagès
    >>> >>>>
    >>> >>>> Program in Computational Biology Division of Public Health
    >>> >>>> Sciences Fred Hutchinson Cancer Research Center 1100 Fairview
    >>> >>>> Ave. N, M1-B514 P.O. Box 19024 Seattle, WA 98109-1024
    >>> >>>>
    >>> >>>> E-mail: hpages at fredhutch.org Phone: (206) 667-5791 Fax: (206)
    >>> >>>> 667-1319
    >>> >>>>
    >>> >>>> ______________________________________________
    >>> >>>> R-devel at r-project.org mailing list
    >>> >>>> 
    >>> https://urldefense.proofpoint.com/v2/url?u=https-3A__stat.ethz.ch_mailman_listinfo_r-2Ddevel&d=DwIFaQ&c=eRAMFD45gAfqt84VtBcfhQ&r=BK7q3XeAvimeWdGbWY_wJYbW0WYiZvSXAJJKaaPhzWA&m=JwgKhKD2k-9Kedeh6pqu-A8x6UEV0INrcxcSGVGo3Tg&s=f7IKJIhpRNJMC3rZAkuI6-MTdL3GAKSV2wK0boFN5HY&e=
    >>> >>>>
    >>> >>>
    >>> >>
    >>> 
    >>> > -- Hervé Pagès
    >>> 
    >>> > Program in Computational Biology Division of Public Health Sciences
    >>> > Fred Hutchinson Cancer Research Center 1100 Fairview Ave. N,
    >>> > M1-B514 P.O. Box 19024 Seattle, WA 98109-1024
    >>> 
    >>> > E-mail: hpages at fredhutch.org Phone: (206) 667-5791 Fax: (206)
    >>> > 667-1319
    >>> 
    >>> > ______________________________________________
    >>> > R-devel at r-project.org mailing list
    >>> > https://stat.ethz.ch/mailman/listinfo/r-devel
    >>> 
    >>> ______________________________________________
    >>> R-devel at r-project.org mailing list
    >>> https://stat.ethz.ch/mailman/listinfo/r-devel
    >> 

    > ______________________________________________
    > R-devel at r-project.org mailing list
    > https://stat.ethz.ch/mailman/listinfo/r-devel



More information about the R-devel mailing list