From m@ech|er @end|ng |rom @t@t@m@th@ethz@ch  Wed Mar  6 09:50:18 2019
From: m@ech|er @end|ng |rom @t@t@m@th@ethz@ch (Martin Maechler)
Date: Wed, 6 Mar 2019 09:50:18 +0100
Subject: [Rd] stopifnot
In-Reply-To: <23678.54840.452841.851948@stat.math.ethz.ch>
References: <625268441.9734255.1551806960886.ref@mail.yahoo.com>
 <625268441.9734255.1551806960886@mail.yahoo.com>
 <23678.54840.452841.851948@stat.math.ethz.ch>
Message-ID: <23679.35274.508559.20674@stat.math.ethz.ch>

>>>>> Martin Maechler 
>>>>>     on Tue, 5 Mar 2019 21:04:08 +0100 writes:

>>>>> Suharto Anggono Suharto Anggono 
>>>>>     on Tue, 5 Mar 2019 17:29:20 +0000 writes:

    >> Another possible shortcut definition:

    >> assert <- function(exprs)
    >> do.call("stopifnot", list(exprs = substitute(exprs), local = parent.frame()))

    > Thank you.  I think this is mostly a matter of taste, but I
    > liked your version using eval() & substitute() a bit more.  For
    > me, do.call() is a heavy hammer I only like to use when needed..

    > Or would there be advantages of this version?
    > Indeed (as you note below) one important consideration is the exact
    > message that is produced when one assertion fails.

    >> After thinking again, I propose to use
    >> ??? ? ? stop(simpleError(msg, call = if(p <- sys.parent()) sys.call(p)))

    > That would of course be considerably simpler indeed,  part "2 a" of these:

    >> - It seems that the call is the call of the frame where stopifnot(...) is evaluated. Because that is the correct context, I think it is good.
    >> - It is simpler and also works for call that originally comes from stopifnot(exprs=*) .

    >> - It allows shortcut ('assert') to have the same call in error message as stopifnot(exprs=*) .

    > That may be another good reason in addition to code simplicity.

    > I will have to see if this extra simplification does not loose
    > more than I'd want.


    >> Another thing: Is it intended that
    >> do.call("stopifnot", list(exprs = expression()))
    >> evaluates each element of the expression object?

    > ??  I really don't know.  Even though such a case looks
    > "unusual" (to say the least), in principle I'd like that
    > expressions are evaluated sequentially until the first non-TRUE
    > result.  With a concrete example, I do like what we have
    > currently in unchanged R-devel, but also in R 3.5.x, i.e., in
    > the following, not any "NOT GOOD" should pop up:

    >> stopifnot(exprs = expression(1==1, 2 < 1, stop("NOT GOOD!\n")))
    > Error: 2 < 1 is not TRUE
    >> do.call(stopifnot, list(exprs = expression(1==1, 2 < 1, stop("NOT GOOD!\n"))))
    > Error in do.call(stopifnot, list(exprs = expression(1 == 1, 2 < 1, cat("NOT GOOD!\n")))) : 
    > 2 < 1 is not TRUE
    >> 

    > Hmm, it seems I do not understand what you ask above in your
    > "Another thing: .."


    >> If so, maybe add a case for 'cl', like
    >> ??? ? ? else if(is.expression(exprs))
    >> ??? ??? as.call(c(quote(expression), exprs))

    > that seems simple indeed, but at the moment, I cannot see one example
    > where it makes a difference ... or then I'm "blind" .. ???

    > Best,
    > Martin

Some more testing of examples lead me to keep the more
sophisticated "computation" of 'n'  for the  sys.call(n-1).

Main reason:  If one of the expression is not all TRUE, I really
don't want to see the full 'stopifnot(....)' call in the printed
error message.
I do want to encourage that  stopifnot()  asserts many things
and so its own call should really not be shown.

Also I really wanted to commit something, notably also fixing
the   stopifnot(exprs = T)  bug,  so R-devel (rev >= 76203 ) now
contains a simpler and much faster  stopifnot() than previously
[and than the R 3.5.x series].

I agree that the final decisions on getting a call (or not --
which was a very good idea by you!) and which parent's call
should be used  may deserve some future tinkering..

Thank you again, Suharto Anggono,
for your contributions to making R better !

Martin



[Rd] stopifnot
Martin Maechler