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.mich...@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@df <- x@df[eval(substitute(subset), x@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@nrow <- nrow(ans@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@df <- x@df[eval(substitute(subset), x@df, env),,drop=FALSE] > x > }) > setMethod("subset", "B", function(x, ...) { > ans <- callNextMethod() > ans@nrow <- nrow(ans@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@r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel ______________________________________________ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel