Here is a patch to function 'stopifnot' that adds 'evaluated' argument and makes 'exprs' argument in 'stopifnot' like 'exprs' argument in 'withAutoprint'. --- stop.R 2019-05-30 14:01:15.282197286 +0000 +++ stop_new.R 2019-05-30 14:01:51.372187466 +0000 @@ -31,7 +31,7 @@ .Internal(stop(call., .makeMessage(..., domain = domain))) } -stopifnot <- function(..., exprs, local = TRUE) +stopifnot <- function(..., exprs, evaluated = FALSE, local = TRUE) { n <- ...length() if(!missing(exprs)) { @@ -41,21 +41,19 @@ else if(isFALSE(local)) .GlobalEnv else if (is.environment(local)) local else stop("'local' must be TRUE, FALSE or an environment") - exprs <- substitute(exprs) # protect from evaluation - E1 <- if(is.call(exprs)) exprs[[1]] + E1 <- if(!evaluated && is.call(exprs <- substitute(exprs))) exprs[[1]] cl <- if(is.symbol(E1) && - (E1 == quote(`{`) || E1 == quote(expression))) { + E1 == quote(`{`)) { exprs[[1]] <- quote(stopifnot) ## --> stopifnot(*, *, ..., *) : exprs } else as.call(c(quote(stopifnot), - if(is.null(E1) && is.symbol(exprs) && - is.expression(E1 <- eval(exprs))) # the *name* of an expression - as.list(E1) + if(is.expression(exprs)) + exprs else as.expression(exprs) - )) # or fail .. + )) names(cl) <- NULL return(eval(cl, envir=envir)) } -------------------------------------------- Subject: Re: [Rd] stopifnot To: "Martin Maechler" <maechler at stat.math.ethz.ch> Cc: r-devel at r-project.org Date: Monday, 15 April, 2019, 2:56 AM Also, in current definition of function 'stopifnot' in R 3.6.0 beta or R devel, for 'cl' if 'exprs' is specified, there a case with comment "the *name* of an expression". The intent is allowing stopifnot(exprs = ee) , where variable 'ee' holds an expression object, to work on the expression object. It is not quite right to use eval(exprs) . It fails when 'stopifnot' is called inside a function, like f <- function(ee) stopifnot(exprs = ee) f(expression()) But, how about local=FALSE case? Should the following work? f <- function(ee) stopifnot(exprs = ee, local = FALSE) f(expression()) But, why bother making it work, while it is undocumented that 'exprs' argument in 'stopifnot' can be an expression? Well, yes, expectation may be set from the name "exprs" itself or from argument 'exprs' in function 'source' or 'withAutoprint'. Function 'withAutoprint' may be the closest match. Function 'withAutoprint' has 'evaluated' argument that controls whether work is on value of 'exprs' or on 'exprs' as given. I like the approach.
>>>>> Suharto Anggono Suharto Anggono >>>>> on Thu, 30 May 2019 14:45:22 +0000 writes: >>>>> Suharto Anggono Suharto Anggono >>>>> on Thu, 30 May 2019 14:45:22 +0000 writes:> Here is a patch to function 'stopifnot' that adds 'evaluated' argument and makes 'exprs' argument in 'stopifnot' like 'exprs' argument in 'withAutoprint'. > --- stop.R 2019-05-30 14:01:15.282197286 +0000 > +++ stop_new.R 2019-05-30 14:01:51.372187466 +0000 [..........] Thank you, Suharto. I've looked at the patch and tested it a bit, and also (re)read your April 15 remarks (cited below). I now agree that my hacks to enable dealing with "language objects" (typically class 'expression') 'exprs' has remained hackish and hence not working in all cases, and that it may be a better idea to add a new logical argument (as in other functions) which has to be "switched" and then leads somewhat simpler and more robust code. OTOH, of course, this is an API change would typically not go into the R 3.6.x series ... and I have no idea if it would affect much more than R's own tests/reg-tests-* ... Even though the argument name 'evaluated' was chosen for withAutoprint(), I don't find it a very good name anymore, and - if the change should happen - would probably prefer something like 'is.language' or 'expr.is.language' or similar.. Could we get any other readers' opinions ? Martin > -------------------------------------------- > On Mon, 15/4/19, Suharto Anggono Suharto Anggono <suharto_anggono at yahoo.com> wrote: > Subject: Re: [Rd] stopifnot > To: "Martin Maechler" <maechler at stat.math.ethz.ch> > Cc: r-devel at r-project.org > Date: Monday, 15 April, 2019, 2:56 AM > Also, in current definition of function 'stopifnot' in R 3.6.0 beta or R devel, for 'cl' if 'exprs' is specified, there a case with comment "the *name* of an expression". The intent is allowing > stopifnot(exprs = ee) , > where variable 'ee' holds an expression object, to work on the expression object. > It is not quite right to use eval(exprs) . It fails when 'stopifnot' is called inside a function, like > f <- function(ee) stopifnot(exprs = ee) > f(expression()) > But, how about local=FALSE case? Should the following work? > f <- function(ee) stopifnot(exprs = ee, local = FALSE) > f(expression()) > But, why bother making it work, while it is undocumented that 'exprs' argument in 'stopifnot' can be an expression? Well, yes, expectation may be set from the name "exprs" itself or from argument 'exprs' in function 'source' or 'withAutoprint'. Function 'withAutoprint' may be the closest match. > Function 'withAutoprint' has 'evaluated' argument that controls whether work is on value of 'exprs' or on 'exprs' as given. I like the approach.
>>>>> Martin Maechler >>>>> on Mon, 3 Jun 2019 18:14:15 +0200 writes:>>>>> Suharto Anggono Suharto Anggono >>>>> on Thu, 30 May 2019 14:45:22 +0000 writes: >>>>> Suharto Anggono Suharto Anggono >>>>> on Thu, 30 May 2019 14:45:22 +0000 writes:>> Here is a patch to function 'stopifnot' that adds 'evaluated' argument and makes 'exprs' argument in 'stopifnot' like 'exprs' argument in 'withAutoprint'. >> --- stop.R 2019-05-30 14:01:15.282197286 +0000 >> +++ stop_new.R 2019-05-30 14:01:51.372187466 +0000 > [..........] > Thank you, Suharto. > I've looked at the patch and tested it a bit, and also (re)read > your April 15 remarks (cited below). I now agree that my hacks to > enable dealing with "language objects" (typically class > 'expression') 'exprs' has remained hackish and hence not > working in all cases, and that it may be a better idea to add > a new logical argument (as in other functions) which has to be > "switched" and then leads somewhat simpler and more robust code. > OTOH, of course, this is an API change would typically not go into the R > 3.6.x series ... and I have no idea if it would affect much more > than R's own tests/reg-tests-* ... > Even though the argument name 'evaluated' was chosen for > withAutoprint(), I don't find it a very good name anymore, and - > if the change should happen - would probably prefer something > like 'is.language' or 'expr.is.language' or similar.. > Could we get any other readers' opinions ? [none ..] In the mean time, I've seen a nicer solution: If have to add yet another argument to stopifnot() to make this cleaner, I'm now pretty sure we should rather use that new argument to pass an "expression-alike" object instead of unevaluated expressions. I'm calling it `exprObject` for now (and ditch the `evaluated=FALSE`). With that the new code becomes even cleaner and easier to understand: stopifnot <- function(..., exprs, exprObject, local = TRUE) { n <- ...length() if((has.e <- !missing(exprs)) || !missing(exprObject)) { if(n || (has.e && !missing(exprObject))) stop("Only one of 'exprs', 'exprObject' or unnamed expressions, not more") envir <- if (isTRUE(local)) parent.frame() else if(isFALSE(local)) .GlobalEnv else if (is.environment(local)) local else stop("'local' must be TRUE, FALSE or an environment") E1 <- if(has.e && is.call(exprs <- substitute(exprs))) exprs[[1]] cl <- if(is.symbol(E1) && E1 == quote(`{`)) { exprs[[1]] <- quote(stopifnot) ## --> stopifnot(*, *, ..., *) : exprs } else as.call(c(quote(stopifnot), if(!has.e) exprObject else as.expression(exprs))) names(cl) <- NULL return(eval(cl, envir=envir)) } ## else use '...' (and not 'exprs') : [............ code unchanged from here to the end .. ............] [............ code unchanged from here to the end .. ............] }