Quantcast

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

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
30 messages Options
12
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

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

Hervé Pagès-2
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: [hidden email]
Phone:  (206) 667-5791
Fax:    (206) 667-1319

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

Peter Dalgaard-2
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 <[hidden email]> 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: [hidden email]
> Phone:  (206) 667-5791
> Fax:    (206) 667-1319
>
> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel

--
Peter Dalgaard, Professor,
Center for Statistics, Copenhagen Business School
Solbjerg Plads 3, 2000 Frederiksberg, Denmark
Phone: (+45)38153501
Office: A 4.23
Email: [hidden email]  Priv: [hidden email]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

Hervé Pagès-2
Not sure why the performance penalty of nonstandard evaluation would
be more of a concern here than for something like switch().

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.

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 <[hidden email]> 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: [hidden email]
>> Phone:  (206) 667-5791
>> Fax:    (206) 667-1319
>>
>> ______________________________________________
>> [hidden email] 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: [hidden email]
Phone:  (206) 667-5791
Fax:    (206) 667-1319

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

Hervé Pagès-2
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.
>
> 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 <[hidden email]> 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: [hidden email]
>>> Phone:  (206) 667-5791
>>> Fax:    (206) 667-1319
>>>
>>> ______________________________________________
>>> [hidden email] 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: [hidden email]
Phone:  (206) 667-5791
Fax:    (206) 667-1319

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

Martin Maechler
>>>>> Hervé Pagès <[hidden email]>
>>>>>     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 <[hidden email]>
    >>>> 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: [hidden email] Phone: (206) 667-5791 Fax: (206)
    >>>> 667-1319
    >>>>
    >>>> ______________________________________________
    >>>> [hidden email] 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: [hidden email] Phone: (206) 667-5791 Fax: (206)
    > 667-1319

    > ______________________________________________
    > [hidden email] mailing list
    > https://stat.ethz.ch/mailman/listinfo/r-devel

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

Serguei Sokol
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 <[hidden email]>
>>>>>>      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 <[hidden email]>
>      >>>> 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: [hidden email] Phone: (206) 667-5791 Fax: (206)
>      >>>> 667-1319
>      >>>>
>      >>>> ______________________________________________
>      >>>> [hidden email] 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: [hidden email] Phone: (206) 667-5791 Fax: (206)
>      > 667-1319
>
>      > ______________________________________________
>      > [hidden email] mailing list
>      > https://stat.ethz.ch/mailman/listinfo/r-devel
>
> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

Serguei Sokol
I see in the archives that the attachment cannot pass.
So, here is the code:
8<----
stopifnot_new <- function (...)
{
     mc <- match.call()
     n <- length(mc)-1
     if (n == 0L)
         return(invisible())
     Dparse <- function(call, cutoff = 60L) {
         ch <- deparse(call, width.cutoff = cutoff)
         if (length(ch) > 1L)
             paste(ch[1L], "....")
         else ch
     }
     head <- function(x, n = 6L) x[seq_len(if (n < 0L) max(length(x) +
         n, 0L) else min(n, length(x)))]
     abbrev <- function(ae, n = 3L) paste(c(head(ae, n), if (length(ae) >
         n) "...."), collapse = "\n  ")
     pfr <- parent.frame()
     for (i in 1L:n) {
         cl.i <- mc[[i + 1L]]
         r <- eval(cl.i, pfr)
         if (!(is.logical(r) && !anyNA(r) && all(r))) {
             msg <- if (is.call(cl.i) && identical(cl.i[[1]], quote(all.equal)) &&
                 (is.null(ni <- names(cl.i)) || length(cl.i) == 3L ||
                     length(cl.i <- cl.i[!nzchar(ni)]) == 3L))
                 sprintf(gettext("%s and %s are not equal:\n  %s"),
                     Dparse(cl.i[[2]]), Dparse(cl.i[[3]]), abbrev(r))
             else sprintf(ngettext(length(r), "%s is not TRUE", "%s are not all TRUE"),
                 Dparse(cl.i))
             stop(msg, call. = FALSE, domain = NA)
         }
     }
     invisible()
}
8<----

Best,
Serguei.

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 <[hidden email]>
>>>>>>>      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 <[hidden email]>
>>      >>>> 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: [hidden email] Phone: (206) 667-5791 Fax: (206)
>>      >>>> 667-1319
>>      >>>>
>>      >>>> ______________________________________________
>>      >>>> [hidden email] 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: [hidden email] Phone: (206) 667-5791 Fax: (206)
>>      > 667-1319
>>
>>      > ______________________________________________
>>      > [hidden email] mailing list
>>      > https://stat.ethz.ch/mailman/listinfo/r-devel
>>
>> ______________________________________________
>> [hidden email] mailing list
>> https://stat.ethz.ch/mailman/listinfo/r-devel
>

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

Martin Maechler
>>>>> Serguei Sokol <[hidden email]>
>>>>>     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 <[hidden email]>
    >>>>>>>> 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 <[hidden email]>
    >>> >>>> 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: [hidden email] Phone: (206) 667-5791 Fax: (206)
    >>> >>>> 667-1319
    >>> >>>>
    >>> >>>> ______________________________________________
    >>> >>>> [hidden email] 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: [hidden email] Phone: (206) 667-5791 Fax: (206)
    >>> > 667-1319
    >>>
    >>> > ______________________________________________
    >>> > [hidden email] mailing list
    >>> > https://stat.ethz.ch/mailman/listinfo/r-devel
    >>>
    >>> ______________________________________________
    >>> [hidden email] mailing list
    >>> https://stat.ethz.ch/mailman/listinfo/r-devel
    >>

    > ______________________________________________
    > [hidden email] mailing list
    > https://stat.ethz.ch/mailman/listinfo/r-devel

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

Peter Dalgaard-2
I think Hervé's idea was just that if switch can evaluate arguments selectively, so can stopifnot(). But switch() is .Primitive, so does it from C.

I think it is almost a no-brainer to implement a sequential stopifnot if dropping to C code is allowed. In R it gets trickier, but how about this:

Stopifnot <- function(...)
{
  n <- length(match.call()) - 1
  for (i in 1:n)
  {
    nm <- as.name(paste0("..",i))
    if (!eval(nm)) stop("not all true")
  }
}
Stopifnot(2+2==4)
Stopifnot(2+2==5, print("Hey!!!") == "Hey!!!")
Stopifnot(2+2==4, print("Hey!!!") == "Hey!!!")
Stopifnot(T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,F,T)


> On 15 May 2017, at 15:37 , Martin Maechler <[hidden email]> wrote:
>
> I'm still curious about Hervé's idea on using  switch()  for the
> issue.

--
Peter Dalgaard, Professor,
Center for Statistics, Copenhagen Business School
Solbjerg Plads 3, 2000 Frederiksberg, Denmark
Phone: (+45)38153501
Office: A 4.23
Email: [hidden email]  Priv: [hidden email]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

Serguei Sokol
In reply to this post by Martin Maechler
Le 15/05/2017 à 15:37, Martin Maechler a écrit :

>>>>>> Serguei Sokol <[hidden email]>
>>>>>>      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.
It can be avoided but at price of customizing error() and warning() calls with something like:
wrn <- function(w) {w$call <- cl.i; warning(w)}
err <- function(e) {e$call <- cl.i; stop(e)}
...
tryCatch(r <- eval(cl.i, pfr), warning=wrn, error=err)

Serguei.

>
> --
>
> 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 <[hidden email]>
>      >>>>>>>> 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 <[hidden email]>
>      >>> >>>> 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: [hidden email] Phone: (206) 667-5791 Fax: (206)
>      >>> >>>> 667-1319
>      >>> >>>>
>      >>> >>>> ______________________________________________
>      >>> >>>> [hidden email] 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: [hidden email] Phone: (206) 667-5791 Fax: (206)
>      >>> > 667-1319
>      >>>
>      >>> > ______________________________________________
>      >>> > [hidden email] mailing list
>      >>> > https://stat.ethz.ch/mailman/listinfo/r-devel
>      >>>
>      >>> ______________________________________________
>      >>> [hidden email] mailing list
>      >>> https://stat.ethz.ch/mailman/listinfo/r-devel
>      >>
>
>      > ______________________________________________
>      > [hidden email] mailing list
>      > https://stat.ethz.ch/mailman/listinfo/r-devel
>

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

Martin Maechler
In reply to this post by Peter Dalgaard-2
>>>>> peter dalgaard <[hidden email]>
>>>>>     on Mon, 15 May 2017 16:28:42 +0200 writes:

    > I think Hervé's idea was just that if switch can evaluate arguments selectively, so can stopifnot(). But switch() is .Primitive, so does it from C.

if he just meant that, then "yes, of course" (but not so interesting).

    > I think it is almost a no-brainer to implement a sequential stopifnot if dropping to C code is allowed. In R it gets trickier, but how about this:

Something like this, yes, that's close to what Serguei Sokol had proposed
(and of course I *do*  want to keep the current sophistication
 of stopifnot(), so this is really too simple)

    > Stopifnot <- function(...)
    > {
    > n <- length(match.call()) - 1
    > for (i in 1:n)
    > {
    > nm <- as.name(paste0("..",i))
    > if (!eval(nm)) stop("not all true")
    > }
    > }
    > Stopifnot(2+2==4)
    > Stopifnot(2+2==5, print("Hey!!!") == "Hey!!!")
    > Stopifnot(2+2==4, print("Hey!!!") == "Hey!!!")
    > Stopifnot(T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,F,T)


    >> On 15 May 2017, at 15:37 , Martin Maechler <[hidden email]> wrote:
    >>
    >> I'm still curious about Hervé's idea on using  switch()  for the
    >> issue.

    > --
    > Peter Dalgaard, Professor,
    > Center for Statistics, Copenhagen Business School
    > Solbjerg Plads 3, 2000 Frederiksberg, Denmark
    > Phone: (+45)38153501
    > Office: A 4.23
    > Email: [hidden email]  Priv: [hidden email]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

Peter Dalgaard-2
However, it doesn't look much of a hassle to fuse my suggestion into the current stopifnot: Basically, just use eval(as.name(paste0("..",i))) instead of ll[[i]] and base the initial calculation of n on match.call() rather than on list(...).

-pd


> On 15 May 2017, at 17:04 , Martin Maechler <[hidden email]> wrote:
>
>>>>>> peter dalgaard <[hidden email]>
>>>>>>    on Mon, 15 May 2017 16:28:42 +0200 writes:
>
>> I think Hervé's idea was just that if switch can evaluate arguments selectively, so can stopifnot(). But switch() is .Primitive, so does it from C.
>
> if he just meant that, then "yes, of course" (but not so interesting).
>
>> I think it is almost a no-brainer to implement a sequential stopifnot if dropping to C code is allowed. In R it gets trickier, but how about this:
>
> Something like this, yes, that's close to what Serguei Sokol had proposed
> (and of course I *do*  want to keep the current sophistication
> of stopifnot(), so this is really too simple)
>
>> Stopifnot <- function(...)
>> {
>> n <- length(match.call()) - 1
>> for (i in 1:n)
>> {
>> nm <- as.name(paste0("..",i))
>> if (!eval(nm)) stop("not all true")
>> }
>> }
>> Stopifnot(2+2==4)
>> Stopifnot(2+2==5, print("Hey!!!") == "Hey!!!")
>> Stopifnot(2+2==4, print("Hey!!!") == "Hey!!!")
>> Stopifnot(T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,F,T)
>
>
>>> On 15 May 2017, at 15:37 , Martin Maechler <[hidden email]> wrote:
>>>
>>> I'm still curious about Hervé's idea on using  switch()  for the
>>> issue.
>
>> --
>> Peter Dalgaard, Professor,
>> Center for Statistics, Copenhagen Business School
>> Solbjerg Plads 3, 2000 Frederiksberg, Denmark
>> Phone: (+45)38153501
>> Office: A 4.23
>> Email: [hidden email]  Priv: [hidden email]
>
>
>
>
>
>
>
>

--
Peter Dalgaard, Professor,
Center for Statistics, Copenhagen Business School
Solbjerg Plads 3, 2000 Frederiksberg, Denmark
Phone: (+45)38153501
Office: A 4.23
Email: [hidden email]  Priv: [hidden email]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

Martin Maechler
In reply to this post by Serguei Sokol
>>>>> Serguei Sokol <[hidden email]>
>>>>>     on Mon, 15 May 2017 16:32:20 +0200 writes:

    > Le 15/05/2017 à 15:37, Martin Maechler a écrit :
    >>>>>>> Serguei Sokol <[hidden email]>
    >>>>>>> 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.

    > It can be avoided but at price of customizing error() and warning() calls with something like:
    > wrn <- function(w) {w$call <- cl.i; warning(w)}
    > err <- function(e) {e$call <- cl.i; stop(e)}
    > ...
    > tryCatch(r <- eval(cl.i, pfr), warning=wrn, error=err)

    > Serguei.

Well, a good idea, but the 'warning' case is more complicated
(and the above incorrect): I do want the warning there, but
_not_ return the warning, but rather, the result of eval() :
So this needs even more sophistication, using  withCallingHandlers(.)
and maybe that really get's too sophisticated and no
more "readable" to 99.9% of the R users ... ?

I now do append my current version -- in case some may want to
comment or improve further.

Martin


stopifnot <- function(...)
{
    penv <- parent.frame()
    cl <- match.call(envir = penv)[-1]
    Dparse <- function(call, cutoff = 60L) {
        ch <- deparse(call, width.cutoff = cutoff)
        if(length(ch) > 1L) paste(ch[1L], "....") else ch
    }
    head <- function(x, n = 6L) ## basically utils:::head.default()
        x[seq_len(if(n < 0L) max(length(x) + n, 0L) else min(n, length(x)))]
    abbrev <- function(ae, n = 3L)
        paste(c(head(ae, n), if(length(ae) > n) "...."), collapse="\n  ")
    benv <- baseenv()
    for (i in seq_along(cl)) {
        cl.i <- cl[[i]]
        ## r <- eval(cl.i, envir = penv, enclos = benv)
        ##      ---- but with correct warn/err messages:
        r <- withCallingHandlers(
            tryCatch(eval(cl.i, envir = penv, enclos = benv),
                     error = function(e) { e$call <- cl.i; stop(e) }),
            warning = function(w) { w$call <- cl.i; w })
        if (!(is.logical(r) && !anyNA(r) && all(r))) {
            msg <- ## special case for decently written 'all.equal(*)':
                if(is.call(cl.i) && identical(cl.i[[1]], quote(all.equal)) &&
                   (is.null(ni <- names(cl.i)) || length(cl.i) == 3L ||
                    length(cl.i <- cl.i[!nzchar(ni)]) == 3L))

                    sprintf(gettext("%s and %s are not equal:\n  %s"),
                            Dparse(cl.i[[2]]),
                            Dparse(cl.i[[3]]), abbrev(r))
                else
                    sprintf(ngettext(length(r),
                                     "%s is not TRUE",
                                     "%s are not all TRUE"),
                            Dparse(cl.i))

            stop(msg, call. = FALSE, domain = NA)
        }
    }
    invisible()
}

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

Serguei Sokol
Le 15/05/2017 à 17:44, Martin Maechler a écrit :
...
> So this needs even more sophistication, using withCallingHandlers(.)
> and maybe that really get's too sophisticated and no
> more "readable" to 99.9% of the R users ... ?
I'd say the current version is of minimal sophistication to reach
both the doc and test requirements.

Serguei.

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

luke-tierney
In reply to this post by Martin Maechler
This is getting pretty convoluted.

The current behavior is consistent with the description at the top of
the help page -- it does not promise to stop evaluation once the first
non-TRUE is found.  That seems OK to me -- if you want sequencing you
can use

stopifnot(A)
stopifnot(B)

or

stopifnot(A && B)

I could see an argument for a change that in the multiple argumetn
case reports _all_ that fail; that would seem more useful to me than
twisting the code into knots.

Best,

luke

On Mon, 15 May 2017, Martin Maechler wrote:

>>>>>> Serguei Sokol <[hidden email]>
>>>>>>     on Mon, 15 May 2017 16:32:20 +0200 writes:
>
>    > Le 15/05/2017 à 15:37, Martin Maechler a écrit :
>    >>>>>>> Serguei Sokol <[hidden email]>
>    >>>>>>> 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.
>
>    > It can be avoided but at price of customizing error() and warning() calls with something like:
>    > wrn <- function(w) {w$call <- cl.i; warning(w)}
>    > err <- function(e) {e$call <- cl.i; stop(e)}
>    > ...
>    > tryCatch(r <- eval(cl.i, pfr), warning=wrn, error=err)
>
>    > Serguei.
>
> Well, a good idea, but the 'warning' case is more complicated
> (and the above incorrect): I do want the warning there, but
> _not_ return the warning, but rather, the result of eval() :
> So this needs even more sophistication, using  withCallingHandlers(.)
> and maybe that really get's too sophisticated and no
> more "readable" to 99.9% of the R users ... ?
>
> I now do append my current version -- in case some may want to
> comment or improve further.
>
> Martin
>
>

--
Luke Tierney
Ralph E. Wareham Professor of Mathematical Sciences
University of Iowa                  Phone:             319-335-3386
Department of Statistics and        Fax:               319-335-3017
    Actuarial Science
241 Schaeffer Hall                  email:   [hidden email]
Iowa City, IA 52242                 WWW:  http://www.stat.uiowa.edu
______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

Hervé Pagès-2
Hi,

On 05/15/2017 10:41 AM, [hidden email] wrote:

> This is getting pretty convoluted.
>
> The current behavior is consistent with the description at the top of
> the help page -- it does not promise to stop evaluation once the first
> non-TRUE is found.  That seems OK to me -- if you want sequencing you
> can use
>
> stopifnot(A)
> stopifnot(B)
>
> or
>
> stopifnot(A && B)

My main use case for using stopifnot() is argument checking. In that
context, I like the conciseness of

   stopifnot(
     A,
     B,
     ...
   )

I think it's a common use case (and a pretty natural thing to do) to
order/organize the expressions in a way such that it only makes sense
to continue evaluating if all was OK so far e.g.

   stopifnot(
     is.numeric(x),
     length(x) == 1,
     is.na(x)
   )

At least that's how things are organized in the stopifnot() calls that
accumulated in my code over the years. That's because I was convinced
that evaluation would stop at the first non-true expression (as
suggested by the man page). Until recently when I got a warning issued
by an expression located *after* the first non-true expression. This
was pretty unexpected/confusing!

If I can't rely on this "sequencing" feature, I guess I can always
do

   stopifnot(A)
   stopifnot(B)
   ...

but I loose the conciseness of calling stopifnot() only once.
I could also use

   stopifnot(A && B && ...)

but then I loose the conciseness of the error message i.e. it's going
to be something like

   Error: A && B && ... is not TRUE

which can be pretty long/noisy compared to the message that reports
only the 1st error.

Conciseness/readability of the single call to stopifnot() and
conciseness of the error message are the features that made me
adopt stopifnot() in the 1st place. If stopifnot() cannot be revisited
to do "sequencing" then that means I will need to revisit all my calls
to stopifnot().

>
> I could see an argument for a change that in the multiple argumetn
> case reports _all_ that fail; that would seem more useful to me than
> twisting the code into knots.

Why not. Still better than the current situation. But only if that
semantic seems more useful to people. Would be sad if usefulness
of one semantic or the other was decided based on trickiness of
implementation.

Thanks,
H.

>
> Best,
>
> luke
>
> On Mon, 15 May 2017, Martin Maechler wrote:
>
>>>>>>> Serguei Sokol <[hidden email]>
>>>>>>>     on Mon, 15 May 2017 16:32:20 +0200 writes:
>>
>>    > Le 15/05/2017 à 15:37, Martin Maechler a écrit :
>>    >>>>>>> Serguei Sokol <[hidden email]>
>>    >>>>>>> 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://urldefense.proofpoint.com/v2/url?u=https-3A__svn.r-2Dproject.org_R_trunk_src_library_base_R_stop.R&d=DwIFAw&c=eRAMFD45gAfqt84VtBcfhQ&r=BK7q3XeAvimeWdGbWY_wJYbW0WYiZvSXAJJKaaPhzWA&m=t9fJDOl9YG2zB-GF0wQXrXJTsW2jxTxMHE-qZfLGzHU&s=KGsvpXrXpHCFTdbLM9ci3sBNO9C3ocsgEqHMvZKvV9I&e=
>>    >> or its corresponding github mirror
>>    >>
>> https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_wch_r-2Dsource_blob_trunk_src_library_base_R_stop.R&d=DwIFAw&c=eRAMFD45gAfqt84VtBcfhQ&r=BK7q3XeAvimeWdGbWY_wJYbW0WYiZvSXAJJKaaPhzWA&m=t9fJDOl9YG2zB-GF0wQXrXJTsW2jxTxMHE-qZfLGzHU&s=7Z5bPVWdGPpY2KLnXQP6c-_8s86CpKe0ZYkCfqjfxY0&e=
>>    >> ]
>>    >>
>>    >> > 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.
>>
>>    > It can be avoided but at price of customizing error() and
>> warning() calls with something like:
>>    > wrn <- function(w) {w$call <- cl.i; warning(w)}
>>    > err <- function(e) {e$call <- cl.i; stop(e)}
>>    > ...
>>    > tryCatch(r <- eval(cl.i, pfr), warning=wrn, error=err)
>>
>>    > Serguei.
>>
>> Well, a good idea, but the 'warning' case is more complicated
>> (and the above incorrect): I do want the warning there, but
>> _not_ return the warning, but rather, the result of eval() :
>> So this needs even more sophistication, using  withCallingHandlers(.)
>> and maybe that really get's too sophisticated and no
>> more "readable" to 99.9% of the R users ... ?
>>
>> I now do append my current version -- in case some may want to
>> comment or improve further.
>>
>> Martin
>>
>>
>

--
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: [hidden email]
Phone:  (206) 667-5791
Fax:    (206) 667-1319

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

Hervé Pagès-2
In reply to this post by Peter Dalgaard-2
On 05/15/2017 07:28 AM, peter dalgaard wrote:
> I think Hervé's idea was just that if switch can evaluate arguments selectively, so can stopifnot().

Yep.

Thanks,
H.

> But switch() is .Primitive, so does it from C.
>
> I think it is almost a no-brainer to implement a sequential stopifnot if dropping to C code is allowed. In R it gets trickier, but how about this:
>
> Stopifnot <- function(...)
> {
>   n <- length(match.call()) - 1
>   for (i in 1:n)
>   {
>     nm <- as.name(paste0("..",i))
>     if (!eval(nm)) stop("not all true")
>   }
> }
> Stopifnot(2+2==4)
> Stopifnot(2+2==5, print("Hey!!!") == "Hey!!!")
> Stopifnot(2+2==4, print("Hey!!!") == "Hey!!!")
> Stopifnot(T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,F,T)
>
>
>> On 15 May 2017, at 15:37 , Martin Maechler <[hidden email]> wrote:
>>
>> I'm still curious about Hervé's idea on using  switch()  for the
>> issue.
>

--
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: [hidden email]
Phone:  (206) 667-5791
Fax:    (206) 667-1319

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

Martin Maechler
In reply to this post by Hervé Pagès-2
>>>>> Hervé Pagès <[hidden email]>
>>>>>     on Mon, 15 May 2017 16:54:46 -0700 writes:

    > Hi,
    > On 05/15/2017 10:41 AM, [hidden email] wrote:
    >> This is getting pretty convoluted.
    >>
    >> The current behavior is consistent with the description at the top of
    >> the help page -- it does not promise to stop evaluation once the first
    >> non-TRUE is found.  That seems OK to me -- if you want sequencing you
    >> can use
    >>
    >> stopifnot(A)
    >> stopifnot(B)
    >>
    >> or
    >>
    >> stopifnot(A && B)

    > My main use case for using stopifnot() is argument checking. In that
    > context, I like the conciseness of

    > stopifnot(
    > A,
    > B,
    > ...
    > )

    > I think it's a common use case (and a pretty natural thing to do) to
    > order/organize the expressions in a way such that it only makes sense
    > to continue evaluating if all was OK so far e.g.

    > stopifnot(
    > is.numeric(x),
    > length(x) == 1,
    > is.na(x)
    > )

I agree.  And that's how I have used stopifnot() in many cases
myself, sometimes even more "extremely" than the above example,
using assertions that only make sense if previous assertions
were fulfilled, such as

    stopifnot(is.numeric(n), length(n) == 1, n == round(n), n >= 0)

or in the Matrix package, first checking some class properties
and then things that only make sense for objects with those properties.


    > At least that's how things are organized in the stopifnot() calls that
    > accumulated in my code over the years. That's because I was convinced
    > that evaluation would stop at the first non-true expression (as
    > suggested by the man page). Until recently when I got a warning issued
    > by an expression located *after* the first non-true expression. This
    > was pretty unexpected/confusing!

    > If I can't rely on this "sequencing" feature, I guess I can always
    > do

    > stopifnot(A)
    > stopifnot(B)
    > ...

    > but I loose the conciseness of calling stopifnot() only once.
    > I could also use

    > stopifnot(A && B && ...)

    > but then I loose the conciseness of the error message i.e. it's going
    > to be something like

    > Error: A && B && ... is not TRUE

    > which can be pretty long/noisy compared to the message that reports
    > only the 1st error.


    > Conciseness/readability of the single call to stopifnot() and
    > conciseness of the error message are the features that made me
    > adopt stopifnot() in the 1st place.

Yes, and that had been my design goal when I created it.

I do tend agree with  Hervé and Serguei here.

    > If stopifnot() cannot be revisited
    > to do "sequencing" then that means I will need to revisit all my calls
    > to stopifnot().

    >>
    >> I could see an argument for a change that in the multiple argumetn
    >> case reports _all_ that fail; that would seem more useful to me than
    >> twisting the code into knots.

Interesting... but really differing from the current documentation,

    > Why not. Still better than the current situation. But only if that
    > semantic seems more useful to people. Would be sad if usefulness
    > of one semantic or the other was decided based on trickiness of
    > implementation.

Well, the trickiness  should definitely play a role.
Apart from functionality and semantics, long term maintenance
and code readibility, even elegance have shown to be very
important aspects of good code in ca 30 years of S and R programming.

OTOH, as mentioned above, the creation of good error messages
has been an important design goal of  stopifnot()  and hence I'm
willing to accept the extra complexity of "patching up" the call
used in the error / warning messages.

Also, as a change to what I posted yesterday, I now plan to follow
Peter Dalgaard's suggestion of using
             eval( ..<n> )
instead of   eval(cl[[i]], envir = <parent.frame(.)>)
as there may be cases where the former behaves better in lazy
evaluation situations.
(Other opinions on that ?)

Martin

    > Thanks,
    > H.

    >>
    >> Best,
    >>
    >> luke
    >>
    >> On Mon, 15 May 2017, Martin Maechler wrote:
    >>
    >>>>>>>> Serguei Sokol <[hidden email]>
    >>>>>>>> on Mon, 15 May 2017 16:32:20 +0200 writes:
    >>>
    >>> > Le 15/05/2017 à 15:37, Martin Maechler a écrit :
    >>> >>>>>>> Serguei Sokol <[hidden email]>
    >>> >>>>>>> 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://urldefense.proofpoint.com/v2/url?u=https-3A__svn.r-2Dproject.org_R_trunk_src_library_base_R_stop.R&d=DwIFAw&c=eRAMFD45gAfqt84VtBcfhQ&r=BK7q3XeAvimeWdGbWY_wJYbW0WYiZvSXAJJKaaPhzWA&m=t9fJDOl9YG2zB-GF0wQXrXJTsW2jxTxMHE-qZfLGzHU&s=KGsvpXrXpHCFTdbLM9ci3sBNO9C3ocsgEqHMvZKvV9I&e=
    >>> >> or its corresponding github mirror
    >>> >>
    >>> https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_wch_r-2Dsource_blob_trunk_src_library_base_R_stop.R&d=DwIFAw&c=eRAMFD45gAfqt84VtBcfhQ&r=BK7q3XeAvimeWdGbWY_wJYbW0WYiZvSXAJJKaaPhzWA&m=t9fJDOl9YG2zB-GF0wQXrXJTsW2jxTxMHE-qZfLGzHU&s=7Z5bPVWdGPpY2KLnXQP6c-_8s86CpKe0ZYkCfqjfxY0&e=
    >>> >> ]
    >>> >>
    >>> >> > 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.
    >>>
    >>> > It can be avoided but at price of customizing error() and
    >>> warning() calls with something like:
    >>> > wrn <- function(w) {w$call <- cl.i; warning(w)}
    >>> > err <- function(e) {e$call <- cl.i; stop(e)}
    >>> > ...
    >>> > tryCatch(r <- eval(cl.i, pfr), warning=wrn, error=err)
    >>>
    >>> > Serguei.
    >>>
    >>> Well, a good idea, but the 'warning' case is more complicated
    >>> (and the above incorrect): I do want the warning there, but
    >>> _not_ return the warning, but rather, the result of eval() :
    >>> So this needs even more sophistication, using  withCallingHandlers(.)
    >>> and maybe that really get's too sophisticated and no
    >>> more "readable" to 99.9% of the R users ... ?
    >>>
    >>> I now do append my current version -- in case some may want to
    >>> comment or improve further.
    >>>
    >>> Martin

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

Serguei Sokol
In reply to this post by luke-tierney
Le 15/05/2017 à 19:41, [hidden email] a écrit :
> This is getting pretty convoluted.
>
> The current behavior is consistent with the description at the top of
> the help page -- it does not promise to stop evaluation once the first
> non-TRUE is found.
Hm... we can read in the man page :
‘stopifnot(A, B)’ is conceptually equivalent to

       { if(any(is.na(A)) || !all(A)) stop(...);
         if(any(is.na(B)) || !all(B)) stop(...) }
and this behavior does promise to stop at first non-TRUE value
without evaluation of the rest of conditions.

Sergueï.

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: stopifnot() does not stop at first non-TRUE argument

luke-tierney
On Tue, 16 May 2017, Serguei Sokol wrote:

> Le 15/05/2017 à 19:41, [hidden email] a écrit :
>> This is getting pretty convoluted.
>>
>> The current behavior is consistent with the description at the top of
>> the help page -- it does not promise to stop evaluation once the first
>> non-TRUE is found.
> Hm... we can read in the man page :
> ‘stopifnot(A, B)’ is conceptually equivalent to
>
>      { if(any(is.na(A)) || !all(A)) stop(...);
>        if(any(is.na(B)) || !all(B)) stop(...) }
> and this behavior does promise to stop at first non-TRUE value
> without evaluation of the rest of conditions.

Yes: that is why I explicitly referenced the description at the top of
the page.

Changing the 'conceptually equivalent' bit to reflect what is
happening is easy.  The changes being discussed, and their long term
maintenance, ar not.

Best,

luke


>
> Sergueï.
>

--
Luke Tierney
Ralph E. Wareham Professor of Mathematical Sciences
University of Iowa                  Phone:             319-335-3386
Department of Statistics and        Fax:               319-335-3017
    Actuarial Science
241 Schaeffer Hall                  email:   [hidden email]
Iowa City, IA 52242                 WWW:  http://www.stat.uiowa.edu
______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
12
Loading...