Hi Dipterix, On Fri, Oct 28, 2022 at 1:10 PM Dipterix Wang <dipterix.wang at gmail.com> wrote:> Hi, > > I was wondering if it is a good idea to delay the evaluation of expression > within invisible(), just like data()/delayedAssign()? > > The idea is a function might return an invisible object. This object might > not be used by the users if the function returns are not assigned nor > passed to another function call. For example, > > f <- function() { > # do something eagerly > > return(invisible({ > # calculate message that might take long/extra memory, but only useful > if printed out > })) > } > > If `f()` is not immediately assigned to a variable, then there is no > reason to evaluate invisible(?). >This is not quite true. The value, even when invisible, is captured by .Last.value, and> f <- function() invisible(5)> f()> .Last.value[1] 5 Now that doesn't actually preclude what you're suggesting (just have to wait for .Last.value to be populated by something else), but it does complicate it to the extent that I'm not sure the benefit we'd get would be worth it. Also, in the case you're describing, you'd be pushing the computational cost into printing, which, imo, is not where it should live. Printing a values generally speaking, should just print things, imo. That said, if you really wanted to do this, you could approach the behavior you want, I believe (but again, I think this is a bad idea) by returning a custom class that wraps formula (or, I imagine, tidyverse style quosures) that reach back into the call frame you return them from, and evaluating them only on demand. Best, ~G> This idea is somewhere between `delayedAssign` and eager evaluation. Maybe > we could call it delayedInvisible()? > > Best, > - Zhengjia > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel >[[alternative HTML version deleted]]
You can play with the idea by returning an environment that contains delayed assignments. E.g.,> f <- function(x) {+ delayedAssign("eval_date", { cat("Evaluating 'date'\n"); date()}) + delayedAssign("sum_x", { cat("Evaluating 'sum_x'\n"); sum(x)}) + environment() + }> fx <- f(1:10) > date()[1] "Fri Oct 28 14:22:12 2022"> Sys.sleep(2) > fx$eval_dateEvaluating 'date' [1] "Fri Oct 28 14:22:24 2022"> Sys.sleep(2) > fx$eval_date[1] "Fri Oct 28 14:22:24 2022"> fx$sum_xEvaluating 'sum_x' [1] 55> fx$sum_x[1] 55 -Bill On Fri, Oct 28, 2022 at 2:11 PM Gabriel Becker <gabembecker at gmail.com> wrote:> Hi Dipterix, > > > On Fri, Oct 28, 2022 at 1:10 PM Dipterix Wang <dipterix.wang at gmail.com> > wrote: > > > Hi, > > > > I was wondering if it is a good idea to delay the evaluation of > expression > > within invisible(), just like data()/delayedAssign()? > > > > The idea is a function might return an invisible object. This object > might > > not be used by the users if the function returns are not assigned nor > > passed to another function call. For example, > > > > f <- function() { > > # do something eagerly > > > > return(invisible({ > > # calculate message that might take long/extra memory, but only > useful > > if printed out > > })) > > } > > > > If `f()` is not immediately assigned to a variable, then there is no > > reason to evaluate invisible(?). > > > > This is not quite true. The value, even when invisible, is captured by > .Last.value, and > > > f <- function() invisible(5) > > > f() > > > .Last.value > > [1] 5 > > > Now that doesn't actually preclude what you're suggesting (just have to > wait for .Last.value to be populated by something else), but it does > complicate it to the extent that I'm not sure the benefit we'd get would be > worth it. > > Also, in the case you're describing, you'd be pushing the computational > cost into printing, which, imo, is not where it should live. Printing a > values generally speaking, should just print things, imo. > > That said, if you really wanted to do this, you could approach the behavior > you want, I believe (but again, I think this is a bad idea) by returning a > custom class that wraps formula (or, I imagine, tidyverse style quosures) > that reach back into the call frame you return them from, and evaluating > them only on demand. > > Best, > ~G > > > > This idea is somewhere between `delayedAssign` and eager evaluation. > Maybe > > we could call it delayedInvisible()? > > > > Best, > > - Zhengjia > > > > ______________________________________________ > > R-devel at r-project.org mailing list > > https://stat.ethz.ch/mailman/listinfo/r-devel > > > > [[alternative HTML version deleted]] > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel >[[alternative HTML version deleted]]
> This is not quite true. The value, even when invisible, is captured by .Last.value, and > > > f <- function() invisible(5) > > f() > > .Last.value > [1] 5I understand .Last.value will capture the function returns, but that only happens in the top-level... I guess? In the followings code, I think .Last.value does not capture the results of f, h, k, l g <- function() { f(); h(); k(); l() return() } g() Maybe I caused confusion by mentioning `invisible` function. I guess it should be a new function (let?s call it `delayed`). The function does not have to be limited to ?printing?. For example, a digest key a <- function(key, value) { map$set(key, value) return(delayed({ digest(value) })) } Or an async evaluation of which the saved result might not be needed if not assigned (detached), or the result will be ?joined? to the main process a <- function(path) { # async f <- future::future({ # calculate, and then write to path saveRDS(?, path) }) return(delayed({ resolve(f) # wait till f to finish readRDS(path) })) } Although I could use wrappers such as formula, quosure, or environment to achieve similar results, there are two major differences 1. There is an extra call to get the lazy-evaluated results (if I do want to resolve it) 2. The returned objects have to contain sort of ?environment? component in it. It can?t just be simple objects like vectors, matrices, lists, ? (also you can't immediately garbage collect the enclosing environment)>From the implementation perspective, the `delayed` object is ready to be garbage collected if not assigned immediately.Best, - D> > This is not quite true. The value, even when invisible, is captured by .Last.value, and > > > f <- function() invisible(5) > > f() > > .Last.value > [1] 5 > > Now that doesn't actually preclude what you're suggesting (just have to wait for .Last.value to be populated by something else), but it does complicate it to the extent that I'm not sure the benefit we'd get would be worth it. > > Also, in the case you're describing, you'd be pushing the computational cost into printing, which, imo, is not where it should live. Printing a values generally speaking, should just print things, imo. > > That said, if you really wanted to do this, you could approach the behavior you want, I believe (but again, I think this is a bad idea) by returning a custom class that wraps formula (or, I imagine, tidyverse style quosures) that reach back into the call frame you return them from, and evaluating them only on demand. > > Best, > ~G > > > This idea is somewhere between `delayedAssign` and eager evaluation. Maybe we could call it delayedInvisible()? > > Best, > - Zhengjia > > ______________________________________________ > R-devel at r-project.org <mailto:R-devel at r-project.org> mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel <https://stat.ethz.ch/mailman/listinfo/r-devel>[[alternative HTML version deleted]]