Dear List, I am working on updating the pense package and refactored some of the methods. I have several functions which take the same arguments, hence I'm sending all these arguments to an internal function, called `parse_args()`. Since I want to evaluate the arguments in the caller's environment, I'm using the following code call <- match.call(expand.dots = TRUE) call[[1]] <- quote(pense:::parse_args) args <- eval.parent(call) Of course, R CMD CHECK complains about the use of `:::`, as it's almost never needed. I think the above usage would fall into that area of "almost", but I'm not sure if (a) there's a better approach and (b) the CRAN team would agree with me. I would have to test (b) by submitting and working with the CRAN team, but I wanted to ask the list first to see if I'm missing something obvious. I don't want to export the function parse_args() as it's not useful for a user, and the use is truly internal. Thanks and all the best, David
[R-pkg-devel] NOTE about use of `:::`
7 messages · David Kepplinger, Simon Urbanek, Bill Dunlap +2 more
David, why not call[[1]] <- parse_args The assignment is evaluated in your namespace so that makes sure the call is that of your function. The only downside I see is that in a stack trace you'll see the definition instead of the name. Or possibly do.call(parse_args, as.list(call[-1])) Cheers, Simon PS: Note that ::: is expensive - it probably doesn't matter here, but would in repeatedly called functions.
On 15/12/2022, at 12:19 PM, David Kepplinger <david.kepplinger at gmail.com> wrote: Dear List, I am working on updating the pense package and refactored some of the methods. I have several functions which take the same arguments, hence I'm sending all these arguments to an internal function, called `parse_args()`. Since I want to evaluate the arguments in the caller's environment, I'm using the following code call <- match.call(expand.dots = TRUE) call[[1]] <- quote(pense:::parse_args) args <- eval.parent(call) Of course, R CMD CHECK complains about the use of `:::`, as it's almost never needed. I think the above usage would fall into that area of "almost", but I'm not sure if (a) there's a better approach and (b) the CRAN team would agree with me. I would have to test (b) by submitting and working with the CRAN team, but I wanted to ask the list first to see if I'm missing something obvious. I don't want to export the function parse_args() as it's not useful for a user, and the use is truly internal. Thanks and all the best, David [[alternative HTML version deleted]]
______________________________________________ R-package-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-package-devel
Thank you both for the suggestions. I would prefer a clean stack trace in case of errors as input errors are caught by this function and hence users might very well see errors emitted from it. It seems more informative to me if the error message would say "Error in .parse_args?" than "Error in new.env(?". But since this solution was suggested by both of you it is likely that CRAN too would demand this or a similar approach instead of using `:::`. I know `:::` is expansive, but the computations that follow are typically at least a few minutes so anything that takes less than a few seconds won't be noticeable. I also thought about using `...` before, but then the signature of the user-facing functions would be incomplete and IDEs won't be able to provide suggestions. Thanks for the help! -- David On Wed, Dec 14, 2022 at 7:11 PM Simon Urbanek <simon.urbanek at r-project.org> wrote:
David, why not call[[1]] <- parse_args The assignment is evaluated in your namespace so that makes sure the call is that of your function. The only downside I see is that in a stack trace you'll see the definition instead of the name. Or possibly do.call(parse_args, as.list(call[-1])) Cheers, Simon PS: Note that ::: is expensive - it probably doesn't matter here, but would in repeatedly called functions.
On 15/12/2022, at 12:19 PM, David Kepplinger <david.kepplinger at gmail.com>
wrote:
Dear List, I am working on updating the pense package and refactored some of the methods. I have several functions which take the same arguments, hence
I'm
sending all these arguments to an internal function, called
`parse_args()`.
Since I want to evaluate the arguments in the caller's environment, I'm using the following code call <- match.call(expand.dots = TRUE) call[[1]] <- quote(pense:::parse_args) args <- eval.parent(call) Of course, R CMD CHECK complains about the use of `:::`, as it's almost never needed. I think the above usage would fall into that area of "almost", but I'm not sure if (a) there's a better approach and (b) the CRAN team would agree with me. I would have to test (b) by submitting and working with the CRAN team, but I wanted to ask the list first to see if I'm missing something obvious. I don't want to export the function parse_args() as it's not useful for a user, and the use is truly
internal.
Thanks and all the best,
David
[[alternative HTML version deleted]]
______________________________________________ R-package-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-package-devel
You could add an 'envir' argument to parse_args() and do your eval(..., envir=envir) stuff inside parse_args(). Then change call[[1]] <- quote(pense:::parse_args) args <- eval.parent(call) to call[[1]] <- quote(parse_args) call$envir <- parent.frame() args <- eval(call) [That code is completely untested.] -Bill On Wed, Dec 14, 2022 at 3:19 PM David Kepplinger <david.kepplinger at gmail.com> wrote:
Dear List,
I am working on updating the pense package and refactored some of the
methods. I have several functions which take the same arguments, hence I'm
sending all these arguments to an internal function, called `parse_args()`.
Since I want to evaluate the arguments in the caller's environment, I'm
using the following code
call <- match.call(expand.dots = TRUE)
call[[1]] <- quote(pense:::parse_args)
args <- eval.parent(call)
Of course, R CMD CHECK complains about the use of `:::`, as it's almost
never needed. I think the above usage would fall into that area of
"almost", but I'm not sure if (a) there's a better approach and (b) the
CRAN team would agree with me. I would have to test (b) by submitting and
working with the CRAN team, but I wanted to ask the list first to see if
I'm missing something obvious. I don't want to export the function
parse_args() as it's not useful for a user, and the use is truly internal.
Thanks and all the best,
David
[[alternative HTML version deleted]]
______________________________________________ R-package-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-package-devel
Here's another suggestion, not sure if it's any good, but you could
structure your functions like
parse_args <- function (envir = parent.frame())
{
evalq(list(a = a, b = b, ..., y = y, z = z), envir)
<...>
}
exported_fun <- function (a, b, ..., y, z)
{
parse_args()
<...>
}
It's seriously ugly, but it could work. You could also do some bquote
substitution
parse_args_expr <- quote(parse_args(a = a, b = b, ..., y = y, z = z))
exported_fun <- function (a, b, ..., y, z) NULL
body(exported_fun) <- bquote({
.(parse_args_expr)
<...>
})
On Wed, Dec 14, 2022, 20:36 David Kepplinger <david.kepplinger at gmail.com>
wrote:
Thank you both for the suggestions. I would prefer a clean stack trace in case of errors as input errors are caught by this function and hence users might very well see errors emitted from it. It seems more informative to me if the error message would say "Error in .parse_args?" than "Error in new.env(?". But since this solution was suggested by both of you it is likely that CRAN too would demand this or a similar approach instead of using `:::`. I know `:::` is expansive, but the computations that follow are typically at least a few minutes so anything that takes less than a few seconds won't be noticeable. I also thought about using `...` before, but then the signature of the user-facing functions would be incomplete and IDEs won't be able to provide suggestions. Thanks for the help! -- David On Wed, Dec 14, 2022 at 7:11 PM Simon Urbanek <simon.urbanek at r-project.org
wrote:
David, why not call[[1]] <- parse_args The assignment is evaluated in your namespace so that makes sure the call is that of your function. The only downside I see is that in a stack
trace
you'll see the definition instead of the name. Or possibly do.call(parse_args, as.list(call[-1])) Cheers, Simon PS: Note that ::: is expensive - it probably doesn't matter here, but would in repeatedly called functions.
On 15/12/2022, at 12:19 PM, David Kepplinger <
david.kepplinger at gmail.com>
wrote:
Dear List, I am working on updating the pense package and refactored some of the methods. I have several functions which take the same arguments, hence
I'm
sending all these arguments to an internal function, called
`parse_args()`.
Since I want to evaluate the arguments in the caller's environment, I'm using the following code call <- match.call(expand.dots = TRUE) call[[1]] <- quote(pense:::parse_args) args <- eval.parent(call) Of course, R CMD CHECK complains about the use of `:::`, as it's almost never needed. I think the above usage would fall into that area of "almost", but I'm not sure if (a) there's a better approach and (b) the CRAN team would agree with me. I would have to test (b) by submitting
and
working with the CRAN team, but I wanted to ask the list first to see
if
I'm missing something obvious. I don't want to export the function parse_args() as it's not useful for a user, and the use is truly
internal.
Thanks and all the best,
David
[[alternative HTML version deleted]]
______________________________________________ R-package-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-package-devel
[[alternative HTML version deleted]]
______________________________________________ R-package-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-package-devel
If you want the name of the function to appear, then you can put the
function in an environment whose parent is where most of the evaluation
should take place. For example,
f <- function(...) {
call <- match.call(expand.dots = TRUE)
call[[1]] <- quote(parse_args)
envir <- new.env(parent = parent.frame())
envir$parse_args <- parse_args
eval(call, envir)
}
parse_args <- function(...) {
cat("args were ", names(list(...)), "\n")
stop("Error in parse_args")
}
f(a = 1, b = 2)
#> args were a b
#> Error in parse_args(a = 1, b = 2): Error in parse_args
Duncan Murdoch
On 14/12/2022 8:35 p.m., David Kepplinger wrote:
Thank you both for the suggestions. I would prefer a clean stack trace in case of errors as input errors are caught by this function and hence users might very well see errors emitted from it. It seems more informative to me if the error message would say "Error in .parse_args?" than "Error in new.env(?". But since this solution was suggested by both of you it is likely that CRAN too would demand this or a similar approach instead of using `:::`. I know `:::` is expansive, but the computations that follow are typically at least a few minutes so anything that takes less than a few seconds won't be noticeable. I also thought about using `...` before, but then the signature of the user-facing functions would be incomplete and IDEs won't be able to provide suggestions. Thanks for the help! -- David On Wed, Dec 14, 2022 at 7:11 PM Simon Urbanek <simon.urbanek at r-project.org> wrote:
David, why not call[[1]] <- parse_args The assignment is evaluated in your namespace so that makes sure the call is that of your function. The only downside I see is that in a stack trace you'll see the definition instead of the name. Or possibly do.call(parse_args, as.list(call[-1])) Cheers, Simon PS: Note that ::: is expensive - it probably doesn't matter here, but would in repeatedly called functions.
On 15/12/2022, at 12:19 PM, David Kepplinger <david.kepplinger at gmail.com>
wrote:
Dear List, I am working on updating the pense package and refactored some of the methods. I have several functions which take the same arguments, hence
I'm
sending all these arguments to an internal function, called
`parse_args()`.
Since I want to evaluate the arguments in the caller's environment, I'm using the following code call <- match.call(expand.dots = TRUE) call[[1]] <- quote(pense:::parse_args) args <- eval.parent(call) Of course, R CMD CHECK complains about the use of `:::`, as it's almost never needed. I think the above usage would fall into that area of "almost", but I'm not sure if (a) there's a better approach and (b) the CRAN team would agree with me. I would have to test (b) by submitting and working with the CRAN team, but I wanted to ask the list first to see if I'm missing something obvious. I don't want to export the function parse_args() as it's not useful for a user, and the use is truly
internal.
Thanks and all the best,
David
[[alternative HTML version deleted]]
______________________________________________ R-package-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-package-devel
[[alternative HTML version deleted]]
______________________________________________ R-package-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-package-devel
4 days later
I think I will go with the suggestion of creating a new empty environment,
adding only the argument parsing function. Moreover I will use the same
name as the user-facing function that's being invoked, i.e.,
foo <- function(...) {
call <- match.call(expand.dots = TRUE)
call[[1]] <- quote(parse_args)
envir <- new.env(parent = parent.frame())
envir$foo <- parse_args
eval(call, envir)
}
This way a trace-back shows the function the user expects, without having
to export the parsing function or duplicate the code in the user-facing
functions.
Thanks again everyone for the suggestions.
All the best,
David
On Thu, Dec 15, 2022 at 4:39 AM Duncan Murdoch <murdoch.duncan at gmail.com>
wrote:
If you want the name of the function to appear, then you can put the
function in an environment whose parent is where most of the evaluation
should take place. For example,
f <- function(...) {
call <- match.call(expand.dots = TRUE)
call[[1]] <- quote(parse_args)
envir <- new.env(parent = parent.frame())
envir$parse_args <- parse_args
eval(call, envir)
}
parse_args <- function(...) {
cat("args were ", names(list(...)), "\n")
stop("Error in parse_args")
}
f(a = 1, b = 2)
#> args were a b
#> Error in parse_args(a = 1, b = 2): Error in parse_args
Duncan Murdoch
On 14/12/2022 8:35 p.m., David Kepplinger wrote:
Thank you both for the suggestions. I would prefer a clean stack trace in case of errors as input errors are caught by this function and hence
users
might very well see errors emitted from it. It seems more informative to
me
if the error message would say "Error in .parse_args?" than "Error in new.env(?". But since this solution was suggested by both of you it is likely that CRAN too would demand this or a similar approach instead of using `:::`. I know `:::` is expansive, but the computations that follow are typically at least a few minutes so anything that takes less than a
few
seconds won't be noticeable. I also thought about using `...` before, but then the signature of the user-facing functions would be incomplete and IDEs won't be able to
provide
suggestions. Thanks for the help! -- David On Wed, Dec 14, 2022 at 7:11 PM Simon Urbanek <
simon.urbanek at r-project.org>
wrote:
David, why not call[[1]] <- parse_args The assignment is evaluated in your namespace so that makes sure the
call
is that of your function. The only downside I see is that in a stack
trace
you'll see the definition instead of the name. Or possibly do.call(parse_args, as.list(call[-1])) Cheers, Simon PS: Note that ::: is expensive - it probably doesn't matter here, but would in repeatedly called functions.
On 15/12/2022, at 12:19 PM, David Kepplinger <
david.kepplinger at gmail.com>
wrote:
Dear List, I am working on updating the pense package and refactored some of the methods. I have several functions which take the same arguments, hence
I'm
sending all these arguments to an internal function, called
`parse_args()`.
Since I want to evaluate the arguments in the caller's environment, I'm using the following code call <- match.call(expand.dots = TRUE) call[[1]] <- quote(pense:::parse_args) args <- eval.parent(call) Of course, R CMD CHECK complains about the use of `:::`, as it's almost never needed. I think the above usage would fall into that area of "almost", but I'm not sure if (a) there's a better approach and (b) the CRAN team would agree with me. I would have to test (b) by submitting
and
working with the CRAN team, but I wanted to ask the list first to see
if
I'm missing something obvious. I don't want to export the function parse_args() as it's not useful for a user, and the use is truly
internal.
Thanks and all the best,
David
[[alternative HTML version deleted]]
______________________________________________ R-package-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-package-devel
[[alternative HTML version deleted]]
______________________________________________ R-package-devel at r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-package-devel