I have a use-case for mapping a function to a vector and then assigning the results to individual objects in the parent environment - not the global environment, but the environment that the map()
was called from. Specifically, this is all happening within a function, so I want to assign these objects to the function's environment for subsequent use within the function call, and only there.
I understand that you can specify environments either by ascending numeric position, with the global as 1, or by counting back from the current environment, namely using rlang::caller_env()
. By either method, though, I have no reliable method of specifying the desired function execution environment in cases like this. As the below reprex shows, I can get it working in one specific case with rlang::caller_env(6)
, but it's obvious that counting exactly 6 frames back happens to work in this one case, with its specific pipe chain and sequence of operations, and any situation could have any other needed value there - I only found that 6 was the right number here by printing the traceback within the function. When using map()
, it's 13 or something, probably because of all the nested function calls within map()
. And I can't get it working at all with list2env()
So what I'm looking for is some argument I can provide to either list2env()
or assign()
that will clearly and consistently cause the assignment to occur specifically in the environment of the function that I'm using them within, even if I call those functions at the end of a pipe chain.
library(tidyverse)
library(rlang, warn.conflicts = FALSE)
## Trying to assign the value 'foo' to an object named 'bar' in a specific
## location
# Fails because `bar` is assigned in the pipe execution evironment
'foo' %>% assign(x = 'bar')
exists('bar')
#> [1] FALSE
# Works because `pos = 1` refers specifically to the global environment
'foo' %>% assign(x = 'bar', pos = 1)
exists('bar')
#> [1] TRUE
rm(bar)
# Works because assign isn't in a pipe, and its special, default `pos` of
# `-1` refers to the function execution environment, as desired
exec(function() {
assign('bar', 'foo')
exists('bar', inherits = FALSE)
})
#> [1] TRUE
rm(bar)
#> Warning in rm(bar): object 'bar' not found
# Fails because the function's exec. env. is "overshot," and the assignment
# occurs in the global environment instead; no numeric position seems to work
exec(function() {
'foo' %>% assign(x = 'bar', pos = 1)
exists('bar', inherits = FALSE)
})
#> [1] FALSE
rm(bar)
# Works, presumably because the function's exec. env. happens to be exactly 6
# frames back from the environment in which the `assign()` call is evaluated, in
# this specific case
exec(function() {
'foo' %>% assign(x = 'bar', pos = caller_env(6))
print(exists('bar', inherits = FALSE))
print(bar)
})
#> [1] TRUE
#> [1] "foo"
# Fails for unknown reasons - maybe there's a `caller_env()` value that would
# work, but I haven't found it
exec(function() {
list <- list(bar = 'foo')
list2env(list, envir = caller_env())
exists('bar', inherits = FALSE)
})
#> [1] FALSE
Created on 2020-10-27 by the reprex package (v0.3.0)
dbplyr
query, involving 9 remote tables, possibly with self-joins, so I want to just be able to refer tofoo
instead oftbl(con, 'foo')
, or evenremote_tbls$foo
, throughout. Though, I guess I could evaluate the whole query expression within aremote_tbls
list, couldn't I? I.e.,with(remote_tbls, {query})
. – DHW