[Rd] getting environment from "top" promise
Romain Francois
romain at r-enthusiasts.com
Wed Feb 12 03:23:31 CET 2014
Hello,
We have something very similar to your while loop in dplyr.
https://github.com/hadley/dplyr/blob/02a609310184d003c2ae9e0c013bfa69fa4d257a/inst/include/tools/DataDots.h#L15
because we need to know exactly in which environment a promise is supposed to be evaluated, even though we might combine standard R evaluation with an alternative faster engine. this is the basis of what we called hybrid evaluation.
For future work, I also have the while loop in the Promise class in Rcpp11, so that when you create a Promise in Rcpp11, its .environment() method gives you what you expect.
https://github.com/romainfrancois/Rcpp11/blob/master/inst/include/Rcpp/Promise.h#L14
So, this is something I find useful, although I’m not sure we are supposed to mess with promises.
Romain
Le 11 févr. 2014 à 19:02, Michael Lawrence <lawrence.michael at gene.com> a écrit :
> Hi all,
>
> It seems that there is a use case for obtaining the environment for the
> "top" promise. By "top", I mean following the promise chain up the call
> stack until hitting a non-promise.
>
> S4 data containers often mimic the API of base R data structures. This
> means writing S4 methods for functions that quote their arguments, like
> with() and subset(). The methods package directly forwards any arguments
> not used for dispatch, so substitute(subset) is able to resolve the
> original quoted argument (this is not the case for naively written
> wrappers). The problem then becomes figuring out the environment in which
> to evaluate the expression.
>
> Consider:
>
> setClass("A", representation(df = "data.frame"))
>
> setMethod("subset", "A", function(x, subset) {
> env <- parent.frame(2)
> x at df <- x at df[eval(substitute(subset), x at df, env),,drop=FALSE]
> x
> })
>
> dropLowMpg <- function(x, cutoff=20) {
> invisible(subset(x, mpg > cutoff))
> }
>
> a <- new("A", df=mtcars)
> dropLowMpg(a)
>
> The above works just fine, because we figured out that we need to evaluate
> in the grand-parent frame to avoid the frame of the generic call. But now
> let's assume A has a subclass B, and subset,B delegates to subset,A via
> callNextMethod(). The call stack is different, and our assumption is
> invalid.
>
> setClass("B", representation(nrow="integer"), contains="A")
> setMethod("subset", "B", function(x, ...) {
> ans <- callNextMethod()
> ans at nrow <- nrow(ans at df)
> ans
> })
> b <- new("B", df=mtcars)
> dropLowMpg(b)
> Error in eval(expr, envir, enclos) (from #3) : object 'cutoff' not found
>
> We can fix this with a simple C function:
> SEXP top_prenv(SEXP nm, SEXP env)
> {
> SEXP promise = findVar(nm, env);
> while(TYPEOF(promise) == PROMSXP) {
> env = PRENV(promise);
> promise = PREXPR(promise);
> }
> return env;
> }
>
> With R wrapper:
> top_prenv <- function(x) {
> .Call2("top_prenv", substitute(x), parent.frame())
> }
>
> Then this works (need to set subset,B again to reset cache):
>
> setMethod("subset", "A", function(x, subset) {
> env <- top_prenv(subset)
> x at df <- x at df[eval(substitute(subset), x at df, env),,drop=FALSE]
> x
> })
> setMethod("subset", "B", function(x, ...) {
> ans <- callNextMethod()
> ans at nrow <- nrow(ans at df)
> ans
> })
>
> b <- new("B", df=mtcars)
> dropLowMpg(b)
>
> Would this be a useful addition to R? Is there a better way to solve this
> issue? We're using this successfully in the IRanges package now, but we'd
> like to avoid dealing with the internal details of R, and this is something
> that could be of general benefit.
>
> Thanks,
> Michael
>
> [[alternative HTML version deleted]]
>
> ______________________________________________
> R-devel at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
More information about the R-devel
mailing list