Home > Enterprise >  Is it possible to have `dput` return source code that would run outside of the enclosing environment
Is it possible to have `dput` return source code that would run outside of the enclosing environment

Time:12-25

Suppose I have a closure add_y(y) which returns a function that adds y to its input.

add_y <- function(y) {
  function(x) {
    x   y
  }
}
add_4 <- add_y(4)

So the value of add_4 is a function that adds 4 to its input. This works. I would like to be use dput to show the definition of add_4 as

function(x) {
  x   4
}

but this is not what dput returns.

add_y <- function(y) {
  function(x) {
    x   y
  }
}
add_4 <- add_y(4)
dput(add_4)
#> function (x) 
#> {
#>     x   y
#> }

Is there a way to obtain source code that would run outside of the enclosing environment?

CodePudding user response:

This can work but it involves changing the contents of add_y.

library(rlang)
library(magrittr)
library(stringr)

add_y <- function(y) {
  fn <- expr(function(x) {
    x !!y
  })
  fn <- deparse(fn) %>% str_c(collapse = "")
  fn <- eval(parse(text = fn))
}

add_4 <- add_y(4)

dput(add_4)
#> function (x) 
#> {
#>     x   4
#> }

Created on 2021-12-24 by the reprex package (v2.0.1)

CodePudding user response:

You could construct a dput replacement that generated code that creates a function like add_4, but it wouldn't deparse the way you want:

dput_with_env <- function(f) {
  fn <- deparse(f, control = c("keepNA", "keepInteger", 
                               "niceNames", "showAttributes"))
  env <- as.list(environment(f))
  cat("local({ f =\n")
  cat(fn, sep = "\n")
  cat("\nenvironment(f) <- list2env(\n")
  dput(env)
  cat(")\nf})")
}

add_y <- function(y) {
  function(x) {
    x   y
  }
}
add_4 <- add_y(4)

dput_with_env(add_4)
#> local({ f =
#> function (x) 
#> {
#>     x   y
#> }
#> 
#> environment(f) <- list2env(
#> list(y = 4)
#> )
#> f})

Created on 2021-12-24 by the reprex package (v2.0.1)

This assumes that the environment of add_4 is quite simple, so the parent of its environment can be the environment in place when you evaluate the code. We can try it out:

newfn <- local({ f =
function (x) 
{
   x   y
}
environment(f) <- list2env(
list(y = 4)
)
f})

newfn
#> function (x) 
#> {
#>    x   y
#> }
#> <environment: 0x7f9a1b5e2318>
newfn(1)
#> [1] 5

Created on 2021-12-24 by the reprex package (v2.0.1)

CodePudding user response:

Not with a dput(), no. The dput() function will not create text representations of environments.

If you want to save the function, you could do

save(add_4, file="add4.Rdata")

and then in another R session

load("add4.Rdata")

That will capture all the enclosed values and your function will behave as it did before

  • Related