Skip to content

transform function is not found when not defined in Globalenv #504

@cderv

Description

@cderv

This was originally posted as a rmarkdown issue:

The general problem is that if rmarkdown::render() is called outside the global R environment, the transform function from another cell is not found.

This could happen in several cases

  • New custom environment for render rmarkdown::render(envir = new.env())
  • Wrapper function my_render <- function() rmarkdown::render("input.Rmd")

They will all fail with an error.

Rmarkdown example with ggplot2

---
title: testing
---

```{r}
neglog_trans <- function(base=10){
  trans <- \(x) -logb(x,base=base)
  inv <- \(x) base^(-x)
  scales::trans_new("neglog", transform = trans, inverse = inv)
}
```

```{r}
trans <- scales:::as.transform("neglog")
str(trans)
```
  • Fails: rmarkdown::render("test.Rmd", envir = new.env())
  • Works: rmarkdown::render("test.Rmd")

Trying to minimize, I got down to scales and this example

evaluate::evaluate(function() {
    neglog_trans <- function(base=10){
        trans <- \(x) -logb(x,base=base)
        inv <- \(x) base^(-x)
        scales::trans_new("neglog", transform = trans, inverse = inv)
    }
    trans <- scales:::as.transform("neglog")
    str(trans)
}, envir = new.env())
#> <evaluation>
#> Source code: 
#>   neglog_trans <- function(base = 10) {
#>       trans <- function(x) -logb(x, base = base)
#>       inv <- function(x) base^(-x)
#>       scales::trans_new("neglog", transform = trans, inverse = inv)
#>   }
#> Source code: 
#>   trans <- scales:::as.transform("neglog")
#> Condition: 
#>   Error in scales:::as.transform("neglog"):
#>   Could not find any function named `transform_neglog()` or
#>   `neglog_trans()`
#> Source code: 
#>   str(trans)
#> Condition: 
#>   Error:
#>   object 'trans' not found

When using default environment, which will be globalenv(), it works ok

evaluate::evaluate(function() {
    neglog_trans <- function(base=10){
        trans <- \(x) -logb(x,base=base)
        inv <- \(x) base^(-x)
        scales::trans_new("neglog", transform = trans, inverse = inv)
    }
    trans <- scales:::as.transform("neglog")
    str(trans)
})
#> <evaluation>
#> Source code: 
#>   neglog_trans <- function(base = 10) {
#>       trans <- function(x) -logb(x, base = base)
#>       inv <- function(x) base^(-x)
#>       scales::trans_new("neglog", transform = trans, inverse = inv)
#>   }
#> Source code: 
#>   trans <- scales:::as.transform("neglog")
#> Source code: 
#>   str(trans)
#> Text output: 
#>   List of 9
#>    $ name        : chr "neglog"
#>    $ transform   :function (x)  
#>    $ inverse     :function (x)  
#>    $ d_transform : NULL
#>    $ d_inverse   : NULL
#>    $ breaks      :function (x, n = n_default)  
#>    $ minor_breaks:function (b, limits, n)  
#>    $ format      :function (x)  
#>    $ domain      : num [1:2] -Inf Inf
#>    - attr(*, "class")= chr "transform"

neglog_trans will be created in the specific envir, but it seems that when auto resolution happens at

scales/R/transform.R

Lines 136 to 145 in 04fc333

# Single characters are interpreted as function names with the
# `transform_`-prefix
f <- paste0("transform_", x)
fun <- get0(f, mode = "function")
# For backward compatibility we preserve `trans_`-prefixes
if (is.null(fun)) {
f2 <- paste0(x, "_trans")
fun <- get0(f2, mode = "function")
}

then the environment tree does not have this specific environment. From debug step at scales:::as.transform, I can see these parents where get0() will look into

Browse[1]> rlang::current_env()
<environment: 0x0000029d7e30a978>
Browse[1]> rlang::env_parents(rlang::current_env())
[[1]] $ <env: namespace:scales>
[[2]] $ <env: imports:scales>
[[3]] $ <env: namespace:base>
[[4]] $ <env: global>

So this won't be looking in the same environment as where the evaluation takes place.

So using this transform feature in the knitr context won't work, unless rendering and evaluation happen in the globalenv.

I don't know if there is something that knitr or evaluate can do better or differently, or how this 'character' to function resolution can run differently.

The workaround is to use the globalenv for now, or directly use the function in transform.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions