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

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
30 messages Options
12
Reply | Threaded
Open this post in threaded view
|

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

luke-tierney
On Tue, 16 May 2017, Martin Maechler wrote:

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

If you go this route it would be useful to step back and think about
whether there might be some useful primitives to add to make this
easier, such as

- provide a dotsLength function for computing the number arguments
   captured in a ... argument

- providing a dotsElt function for extracting the i-the element
   instead of going through the eval(sprintf("..%d", i)) construct.

- maybe something for extracting the expression for the i-th argument.

The might be more generally useful and make the code more readable and
maintainable.

Best,

luke

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

--
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
Reply | Threaded
Open this post in threaded view
|

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

R devel mailing list
In reply to this post by Hervé Pagès-2
switch(i, ...)
extracts 'i'-th argument in '...'. It is like
eval(as.name(paste0("..", i))) .

Just mentioning other things:
- For 'n',
n <- nargs()
can be used.
- sys.call() can be used in place of match.call() .
---------------------------
>>>>> peter dalgaard <pdalgd at gmail.com>
>>>>>     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 <maechler at stat.math.ethz.ch> 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: pd.mes at cbs.dk  Priv: PDalgd at gmail.com

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

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

Peter Dalgaard-2

> On 16 May 2017, at 18:37 , Suharto Anggono Suharto Anggono via R-devel <[hidden email]> wrote:
>
> switch(i, ...)
> extracts 'i'-th argument in '...'. It is like
> eval(as.name(paste0("..", i))) .

Hey, that's pretty neat!

-pd

>
> Just mentioning other things:
> - For 'n',
> n <- nargs()
> can be used.
> - sys.call() can be used in place of match.call() .
> ---------------------------
>>>>>> peter dalgaard <pdalgd at gmail.com>
>>>>>>    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 <maechler at stat.math.ethz.ch> 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: pd.mes at cbs.dk  Priv: PDalgd at gmail.com
>
> ______________________________________________
> [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
Reply | Threaded
Open this post in threaded view
|

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

Martin Maechler
In reply to this post by luke-tierney
>>>>>   <[hidden email]>
>>>>>     on Tue, 16 May 2017 09:49:56 -0500 writes:

    > On Tue, 16 May 2017, Martin Maechler wrote:
    >>>>>>> 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 ?)

    > If you go this route it would be useful to step back and think about
    > whether there might be some useful primitives to add to make this
    > easier, such as

    > - provide a dotsLength function for computing the number arguments
    > captured in a ... argument

actually my current version did not use that anymore
(and here we could use   nargs() )

    > - providing a dotsElt function for extracting the i-the element
    > instead of going through the eval(sprintf("..%d", i)) construct.

I've started to do that, ...

    > - maybe something for extracting the expression for the i-th argument.

well, yes, but that seems quite readable here, we use the whole
call anyway and loop, looking at each sequentially --- that part has not
been new!

    > The might be more generally useful and make the code more readable and
    > maintainable.

    > Best,

    > luke

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

    > --
    > 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
Reply | Threaded
Open this post in threaded view
|

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/16/2017 09:59 AM, peter dalgaard wrote:
>
>> On 16 May 2017, at 18:37 , Suharto Anggono Suharto Anggono via R-devel <[hidden email]> wrote:
>>
>> switch(i, ...)
>> extracts 'i'-th argument in '...'. It is like
>> eval(as.name(paste0("..", i))) .
>
> Hey, that's pretty neat!

Indeed! Seems like this topic is even more connected to switch()
than I anticipated...

H.

>
> -pd
>
>>
>> Just mentioning other things:
>> - For 'n',
>> n <- nargs()
>> can be used.
>> - sys.call() can be used in place of match.call() .
>> ---------------------------
>>>>>>> peter dalgaard <pdalgd at gmail.com>
>>>>>>>    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 <maechler at stat.math.ethz.ch> 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: pd.mes at cbs.dk  Priv: PDalgd at gmail.com
>>
>> ______________________________________________
>> [hidden email] mailing list
>> https://urldefense.proofpoint.com/v2/url?u=https-3A__stat.ethz.ch_mailman_listinfo_r-2Ddevel&d=DwIGaQ&c=eRAMFD45gAfqt84VtBcfhQ&r=BK7q3XeAvimeWdGbWY_wJYbW0WYiZvSXAJJKaaPhzWA&m=mLJLORFCunDiCafHllurGVVVHiMf85ExkM7B5DngfIk&s=helOsmplADBmY6Ct7r30onNuD8a6GKz6yuSgjPxljeU&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
Reply | Threaded
Open this post in threaded view
|

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

Martin Maechler
In reply to this post by R devel mailing list
>>>>> Suharto Anggono Suharto Anggono via R-devel <[hidden email]>
>>>>>     on Tue, 16 May 2017 16:37:45 +0000 writes:

    > switch(i, ...)
    > extracts 'i'-th argument in '...'. It is like
    > eval(as.name(paste0("..", i))) .

Yes, that's neat.

It is only almost the same:  in the case of illegal 'i'
the switch() version returns
    invisible(NULL)

whereas the version we'd want should signal an error, typically
the same error message as

  > t2 <- function(...) ..2
  > t2(1)
  Error in t2(1) (from #1) : the ... list does not contain 2 elements
  >


    > Just mentioning other things:
    > - For 'n',
    > n <- nargs()
    > can be used.

I know .. [in this case, where '...' is the only formal argument of the function]

    > - sys.call() can be used in place of match.call() .

Hmm... in many cases, yes.... notably, as we do *not* want the
argument names here, I think you are right.


    > ---------------------------
>>>>> peter dalgaard <pdalgd at gmail.com>
>>>>>     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 <maechler at stat.math.ethz.ch> 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: pd.mes at cbs.dk  Priv: PDalgd at gmail.com

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

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

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

R devel mailing list
In reply to this post by Hervé Pagès-2
From an example in http://www.uni-muenster.de/ZIV.BennoSueselbeck/s-html/helpfiles/nargs.html , number of arguments in '...' can be obtained by
(function(...)nargs())(...) .

I now realize that sys.call() doesn't expand '...' when the function is called with '...'. It just returns the call as is. If 'stopifnot' uses sys.call() instead of match.call() , the following example behaves improperly:
g <- function(...) stopifnot(...)
g(TRUE, FALSE)

--------------------------------------------
On Thu, 18/5/17, Martin Maechler <[hidden email]> wrote:

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

 Cc: [hidden email]
 Date: Thursday, 18 May, 2017, 3:03 PM
 
>>>>> Suharto Anggono Suharto Anggono via R-devel <r-devel at r-project.org>
>>>>>     on Tue, 16 May 2017 16:37:45 +0000 writes:

    > switch(i, ...)
    > extracts 'i'-th argument in '...'. It is like
    > eval(as.name(paste0("..", i))) .

Yes, that's neat.

It is only almost the same:  in the case of illegal 'i'
the switch() version returns
    invisible(NULL)

whereas the version we'd want should signal an error, typically
the same error message as

  > t2 <- function(...) ..2
  > t2(1)
  Error in t2(1) (from #1) : the ... list does not contain 2 elements
  >


    > Just mentioning other things:
    > - For 'n',
    > n <- nargs()
    > can be used.

I know .. [in this case, where '...' is the only formal argument of the function]

    > - sys.call() can be used in place of match.call() .

Hmm... in many cases, yes.... notably, as we do *not* want the
argument names here, I think you are right.

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

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

Martin Maechler
>>>>> Suharto Anggono Suharto Anggono via R-devel <[hidden email]>
>>>>>     on Thu, 18 May 2017 16:27:09 +0000 writes:

    >> From an example in

    >> http://www.uni-muenster.de/ZIV.BennoSueselbeck/s-html/helpfiles/nargs.html
    >> , number of arguments in '...' can be obtained by

    > (function(...)nargs())(...) .

neat and good.  Though really is not exactly "well readable".

In the mean time, there is   ...length()   in R-devel [somewhat experimentally]

    > I now realize that sys.call() doesn't expand '...' when
    > the function is called with '...'. It just returns the call as is.
yes.
    > If 'stopifnot' uses sys.call() instead of
    > match.call() , the following example behaves improperly:

    > g <- function(...) stopifnot(...)
    > g(TRUE, FALSE)

Indeed.  Very improperly (it does not stop).

However, calling stopifnot() with a '...' passed from above is
not a very good idea anyway, because stopifnot has to assume it
is called with explicit expressions.
Hence we have

  > g <- function(...) stopifnot(...) ;  g(1 == 1, 3 < 1)
  Error: ..2 is not TRUE

{and to "fix" this, e.g., with an extra optional argument} would
 lead to more complications  which I really think we do not want}.

But the example does show we should keep match.call().
Martin

    > --------------------------------------------
    > On Thu, 18/5/17, Martin Maechler
    > <[hidden email]> wrote:

    >  Subject: Re: [Rd] stopifnot() does not stop at first
    > non-TRUE argument

    >  Cc: [hidden email] Date: Thursday, 18 May, 2017,
    > 3:03 PM
 
>>>>> Suharto Anggono Suharto Anggono via R-devel <r-devel at r-project.org>
>>>>>     on Tue, 16 May 2017 16:37:45 +0000 writes:

    >> switch(i, ...)  extracts 'i'-th argument in '...'. It is
    >> like eval(as.name(paste0("..", i))) .

    > Yes, that's neat.

    > It is only almost the same: in the case of illegal 'i' the
    > switch() version returns invisible(NULL)

    > whereas the version we'd want should signal an error,
    > typically the same error message as

    >> t2 <- function(...) ..2 t2(1)
    >   Error in t2(1) (from #1) : the ... list does not contain
    > 2 elements
    >>


    >> Just mentioning other things: - For 'n', n <- nargs() can
    >> be used.

    > I know .. [in this case, where '...' is the only formal
    > argument of the function]

    >> - sys.call() can be used in place of match.call() .

    > Hmm... in many cases, yes.... notably, as we do *not* want
    > the argument names here, I think you are right.

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

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

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

R devel mailing list
While you are fiddling with stopifnot(), please consider changing the form
of the error thrown so that it includes the caller's call.  The change
would be from something like
  stop( <<the message>> )
to
  stop(simpleError( <<the message>>, sys.call(-1)))

For the following code
  f <- function(x, y) {
      stopifnot(x > y)
      x - y
  }
  g <- function(x, y, z) {
      c(f(x, y), f(y, z))
  }
  g(6,3,4)
you would see
  Error in f(y, z) : x > y is not TRUE
instead of the less informative
  Error: x > y is not TRUE



Bill Dunlap
TIBCO Software
wdunlap tibco.com

On Fri, May 19, 2017 at 5:31 AM, Martin Maechler <[hidden email]
> wrote:

> >>>>> Suharto Anggono Suharto Anggono via R-devel <[hidden email]>
> >>>>>     on Thu, 18 May 2017 16:27:09 +0000 writes:
>
>     >> From an example in
>
>     >> http://www.uni-muenster.de/ZIV.BennoSueselbeck/s-html/
> helpfiles/nargs.html
>     >> , number of arguments in '...' can be obtained by
>
>     > (function(...)nargs())(...) .
>
> neat and good.  Though really is not exactly "well readable".
>
> In the mean time, there is   ...length()   in R-devel [somewhat
> experimentally]
>
>     > I now realize that sys.call() doesn't expand '...' when
>     > the function is called with '...'. It just returns the call as is.
> yes.
>     > If 'stopifnot' uses sys.call() instead of
>     > match.call() , the following example behaves improperly:
>
>     > g <- function(...) stopifnot(...)
>     > g(TRUE, FALSE)
>
> Indeed.  Very improperly (it does not stop).
>
> However, calling stopifnot() with a '...' passed from above is
> not a very good idea anyway, because stopifnot has to assume it
> is called with explicit expressions.
> Hence we have
>
>   > g <- function(...) stopifnot(...) ;  g(1 == 1, 3 < 1)
>   Error: ..2 is not TRUE
>
> {and to "fix" this, e.g., with an extra optional argument} would
>  lead to more complications  which I really think we do not want}.
>
> But the example does show we should keep match.call().
> Martin
>
>     > --------------------------------------------
>     > On Thu, 18/5/17, Martin Maechler
>     > <[hidden email]> wrote:
>
>     >  Subject: Re: [Rd] stopifnot() does not stop at first
>     > non-TRUE argument
>
>     >  Cc: [hidden email] Date: Thursday, 18 May, 2017,
>     > 3:03 PM
>
> >>>>> Suharto Anggono Suharto Anggono via R-devel <r-devel at
> r-project.org>
> >>>>>     on Tue, 16 May 2017 16:37:45 +0000 writes:
>
>     >> switch(i, ...)  extracts 'i'-th argument in '...'. It is
>     >> like eval(as.name(paste0("..", i))) .
>
>     > Yes, that's neat.
>
>     > It is only almost the same: in the case of illegal 'i' the
>     > switch() version returns invisible(NULL)
>
>     > whereas the version we'd want should signal an error,
>     > typically the same error message as
>
>     >> t2 <- function(...) ..2 t2(1)
>     >   Error in t2(1) (from #1) : the ... list does not contain
>     > 2 elements
>     >>
>
>
>     >> Just mentioning other things: - For 'n', n <- nargs() can
>     >> be used.
>
>     > I know .. [in this case, where '...' is the only formal
>     > argument of the function]
>
>     >> - sys.call() can be used in place of match.call() .
>
>     > Hmm... in many cases, yes.... notably, as we do *not* want
>     > the argument names here, I think you are right.
>
>     > ______________________________________________
>     > [hidden email] mailing list
>     > https://stat.ethz.ch/mailman/listinfo/r-devel
>
> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>

        [[alternative HTML version deleted]]

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

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

Martin Maechler
>>>>> William Dunlap <[hidden email]>
>>>>>     on Fri, 19 May 2017 09:30:42 -0700 writes:

    > While you are fiddling with stopifnot(), please consider changing the form
    > of the error thrown so that it includes the caller's call.  The change
    > would be from something like
    >   stop( <<the message>> )
    > to
    >   stop(simpleError( <<the message>>, sys.call(-1)))

    > For the following code
    >   f <- function(x, y) {
    >     stopifnot(x > y)
    >     x - y
    >   }
    >   g <- function(x, y, z) {
    >      c(f(x, y), f(y, z))
    >   }
    >   g(6,3,4)

    > you would see
    >   Error in f(y, z) : x > y is not TRUE
    > instead of the less informative
    >   Error: x > y is not TRUE

well, yes, I have been fiddling .. ;-)

and your proposal above is quite remarkable!
I hadn't been aware of (the consequence of) this possibility.

I will do that change in addition to the planned ones, just to
make the changes slightly more modular.
(It will need a change in   tests/isas-tests.Rout.save  as
indeed, the _messages_ of non-toplevel  stopifnot() calls will
change too.
This may affect package checks check for the _wording_ of
stopifnot error messages [which may not be the best idea
.. though understandable for regression checks].

Martin


    > Bill Dunlap
    > TIBCO Software
    > wdunlap tibco.com

    > On Fri, May 19, 2017 at 5:31 AM, Martin Maechler <[hidden email]
    >> wrote:

    >> >>>>> Suharto Anggono Suharto Anggono via R-devel <[hidden email]>
    >> >>>>>     on Thu, 18 May 2017 16:27:09 +0000 writes:
    >>
    >> >> From an example in
    >>
    >> >> http://www.uni-muenster.de/ZIV.BennoSueselbeck/s-html/
    >> helpfiles/nargs.html
    >> >> , number of arguments in '...' can be obtained by
    >>
    >> > (function(...)nargs())(...) .
    >>
    >> neat and good.  Though really is not exactly "well readable".
    >>
    >> In the mean time, there is   ...length()   in R-devel [somewhat
    >> experimentally]
    >>
    >> > I now realize that sys.call() doesn't expand '...' when
    >> > the function is called with '...'. It just returns the call as is.
    >> yes.
    >> > If 'stopifnot' uses sys.call() instead of
    >> > match.call() , the following example behaves improperly:
    >>
    >> > g <- function(...) stopifnot(...)
    >> > g(TRUE, FALSE)
    >>
    >> Indeed.  Very improperly (it does not stop).
    >>
    >> However, calling stopifnot() with a '...' passed from above is
    >> not a very good idea anyway, because stopifnot has to assume it
    >> is called with explicit expressions.
    >> Hence we have
    >>
    >> > g <- function(...) stopifnot(...) ;  g(1 == 1, 3 < 1)
    >> Error: ..2 is not TRUE
    >>
    >> {and to "fix" this, e.g., with an extra optional argument} would
    >> lead to more complications  which I really think we do not want}.
    >>
    >> But the example does show we should keep match.call().
    >> Martin
    >>
    >> > --------------------------------------------
    >> > On Thu, 18/5/17, Martin Maechler
    >> > <[hidden email]> wrote:
    >>
    >> >  Subject: Re: [Rd] stopifnot() does not stop at first
    >> > non-TRUE argument
    >>
    >> >  Cc: [hidden email] Date: Thursday, 18 May, 2017,
    >> > 3:03 PM
    >>
    >> >>>>> Suharto Anggono Suharto Anggono via R-devel <r-devel at
    r-project.org>
    >> >>>>>     on Tue, 16 May 2017 16:37:45 +0000 writes:
    >>
    >> >> switch(i, ...)  extracts 'i'-th argument in '...'. It is
    >> >> like eval(as.name(paste0("..", i))) .
    >>
    >> > Yes, that's neat.
    >>
    >> > It is only almost the same: in the case of illegal 'i' the
    >> > switch() version returns invisible(NULL)
    >>
    >> > whereas the version we'd want should signal an error,
    >> > typically the same error message as
    >>
    >> >> t2 <- function(...) ..2 t2(1)
    >> >   Error in t2(1) (from #1) : the ... list does not contain
    >> > 2 elements
    >> >>
    >>
    >>
    >> >> Just mentioning other things: - For 'n', n <- nargs() can
    >> >> be used.
    >>
    >> > I know .. [in this case, where '...' is the only formal
    >> > argument of the function]
    >>
    >> >> - sys.call() can be used in place of match.call() .
    >>
    >> > Hmm... in many cases, yes.... notably, as we do *not* want
    >> > the argument names here, I think you are right.
    >>
    >> > ______________________________________________
    >> > [hidden email] mailing list
    >> > https://stat.ethz.ch/mailman/listinfo/r-devel
    >>
    >> ______________________________________________
    >> [hidden email] mailing list
    >> https://stat.ethz.ch/mailman/listinfo/r-devel
    >>

    > [[alternative HTML version deleted]]

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