Partialised glue function does not work when called in different environment

Viewed 162

I've partialised the glue function in a project I'm working on so that we can use agreed-on delimiters without having to tell glue about them all the time. But when we go to use the partialised function inside another function, it stops working:

library(purrr)
library(glue)

glue_query <- partial(glue, .open = "<<", .close = ">>")

# case 1: use partialised function in same scope
x <- 15
glue_query("There are <<x>> apples here!")
#> There are 15 apples here!

# case 2: use partialised function in different scope
myfunc_partialised <- function(y) {
  glue_query("The thing I called y is actually <<y>>")
}
myfunc_partialised(15)
#> Error in eval(parse(text = text, keep.source = FALSE), envir): object 'y' not found

# case 3: use function directly
myfunc_regular <- function(z) {
  glue("The thing I called z is actually <<z>>", .open = "<<", .close = ">>")
}
myfunc_regular(15)
#> The thing I called z is actually 15

Created on 2021-07-09 by the reprex package (v2.0.0)

I get the sense that glue_query is looking for objects to interpolate in the environment it was defined in, rather than in the environment it's called in. Is that's what happening here? Can I direct it to use the calling environment? I'd like to use it throughout my package!

EDIT: I understand that glue has an .envir argument that controls which environment the expressions are evaluated in, but I'm not quite sure what to use to ensure it plays nice here!

2 Answers

It does seem that partial() makes it much more difficult to get the correct environment. Instead you can write your own wrapper

glue_query <- function(..., .open = "<<", .close = ">>", .envir=parent.frame()) {
  glue(..., .open=.open, .close=.close, .envir=.envir)
}

This will work with both the test cases you provided.

One alternative is to change the defaults of glue, define your function, the reset the defaults:

library(glue)
library(default)

default(glue) <- list(.open = "<<", .close = ">>")
glue2 <- glue
reset_default(glue)
#> function (..., .sep = "", .envir = parent.frame(), .open = "{", 
#>     .close = "}", .na = "NA", .transformer = identity_transformer, 
#>     .trim = TRUE) 
#> {
#>     glue_data(.x = NULL, ..., .sep = .sep, .envir = .envir, .open = .open, 
#>         .close = .close, .na = .na, .transformer = .transformer, 
#>         .trim = .trim)
#> }
#> <bytecode: 0x0000000014c59d80>
#> <environment: namespace:glue>

# case 1: use partialised function in same scope
x <- 15
glue_query("There are <<x>> apples here!")
#> Error in glue_query("There are <<x>> apples here!"): could not find function "glue_query"
#> There are 15 apples here!

# case 2: use partialised function in different scope
myfunc_partialised <- function(y) {
  glue2("The thing I called y is actually <<y>>")
}
myfunc_partialised(15)
#> The thing I called y is actually 15

# case 3: use function directly
myfunc_regular <- function(z) {
  glue("The thing I called z is actually <<z>>", .open = "<<", .close = ">>")
}
myfunc_regular(15)
#> The thing I called z is actually 15

Created on 2021-07-09 by the reprex package (v0.3.0)

Related