Skip to content

Commit 2198a89

Browse files
committed
Allow extra sources of pronouns in f_eval()
1 parent 11a8069 commit 2198a89

File tree

3 files changed

+69
-4
lines changed

3 files changed

+69
-4
lines changed

R/f-eval.R

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,24 @@
1010
#' \code{.env} and \code{.data}. These are thin wrappers around \code{.data}
1111
#' and \code{.env} that throw errors if you try to access non-existent values.
1212
#'
13+
#' You can also provide additional sources of data by supplying named
14+
#' lists or data frames in \code{...}. Each of these arguments will
15+
#' get a pronoun based on the argument name. These names are
16+
#' automatically prefixed with a dot. Contrarily to \code{data}, these
17+
#' additional sources are not directly accessible. Therefore you have
18+
#' to explicitly qualify them with the relevant pronoun to access
19+
#' these data.
20+
#'
1321
#' @param f A one-sided formula. Any expressions wrapped in \code{ uq() } will
1422
#' will be "unquoted", i.e. they will be evaluated, and the results inserted
1523
#' back into the formula. See \code{\link{f_interp}} for more details.
1624
#' @param data A list (or data frame). \code{find_data} is a generic used to
1725
#' find the data associated with a given object. If you want to make
1826
#' \code{f_eval} work for your own objects, you can define a method for this
1927
#' generic.
28+
#' @param ... Additional sources of pronouns. These should be named
29+
#' lists or data frames. The names of the pronouns are automatically
30+
#' prefixed with a dot.
2031
#' @param x An object for which you want to find associated data.
2132
#' @export
2233
#' @examples
@@ -40,6 +51,13 @@
4051
#' f_eval(~ .data$cyl, mtcars)
4152
#' f_eval(~ .env$cyl, mtcars)
4253
#'
54+
#' # Additional pronouns can be added by supplying explicit:
55+
#' f_eval(~ .iris$Species, iris = iris)
56+
#'
57+
#' # The data contained in these sources can only be accessed
58+
#' # explicitly, through the pronoun. Direct access will fail:
59+
#' \dontrun{f_eval(~ Species, iris = iris)}
60+
#'
4361
#' # Imagine you are computing the mean of a variable:
4462
#' f_eval(~ mean(cyl), mtcars)
4563
#' # How can you change the variable that's being computed?
@@ -56,15 +74,29 @@
5674
#'
5775
#' # Instead we need to use the prefix form of `$`.
5876
#' f_eval(~ mean( `$`(.data, uq(var) )), mtcars)
59-
f_eval <- function(f, data = NULL) {
77+
f_eval <- function(f, data = NULL, ...) {
6078
expr <- f_rhs(f_interp(f))
6179

6280
data <- find_data(data)
6381
env <- environment(f)
82+
explicit <- list(...)
83+
84+
if (length(explicit)) {
85+
if (is.null(names(explicit))) {
86+
stop("`explicit` should be named", call. = FALSE)
87+
}
88+
if (!all(vapply(explicit, is.list, logical(1)))) {
89+
stop("`explicit` should contain lists or data frames", call. = FALSE)
90+
}
91+
}
92+
names(explicit) <- vapply(names(explicit),
93+
function(x) paste0(".", x), character(1))
6494

6595
expr_env <- new.env(parent = env)
66-
expr_env$.env <- complain(env)
67-
expr_env$.data <- complain(data)
96+
sources <- c(.env = env, .data = list(data), explicit)
97+
for (source in names(sources)) {
98+
expr_env[[source]] <- complain(sources[[source]])
99+
}
68100

69101
eval(expr, data, expr_env)
70102
}

man/f_eval.Rd

Lines changed: 20 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-f-eval.R

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,17 @@ test_that("f_eval does quasiquoting", {
3636
x <- 10
3737
expect_equal(f_eval(~ uq(quote(x))), 10)
3838
})
39+
40+
test_that("look into explicit pronouns", {
41+
expect_equal(f_eval(~ .mtcars$cyl, mtcars = mtcars), mtcars$cyl)
42+
})
43+
44+
test_that("data of explicit pronouns cannot be directly accessed", {
45+
expect_error(f_eval(~ cyl, mtcars = mtcars), "not found")
46+
})
47+
48+
test_that("fails when explicit is malformed", {
49+
expect_error(f_eval(~ ., iris, mtcars), "should be named")
50+
expect_error(f_eval(~ ., mtcars = "string"), "should contain lists")
51+
})
52+

0 commit comments

Comments
 (0)