Meta-programming data-mask column aliases

I'm playing around with some code for a data frame in which a particular "role" ("foo" below) has been defined to refer to a specific column (bar) at construction time:

set_foo_col <- \(x, col) `attr<-`(x, "foo", col)
get_foo_col <- \(x) attr(x, "foo")

data <- tibble::tibble(bar = 1:10) |>
  set_foo_col("bar")

I wanted to save myself from having to write a bunch of code like dplyr::mutate(x, baz = .data[[get_foo_col(x)]]), instead hoping I could do some meta-programming shenanigans to use .foo as an alias in data-mask contexts (i.e. dplyr::mutate(x, baz = .foo) instead).

This naïve solution fails, but expresses my intent, I hope:

my_mutate <- function(.data, ...) {
  data <- rlang::enquo(.data)
  dots <- rlang::enquos(...)
  expr <- rlang::expr(dplyr::mutate(!!data, !!!dots))
  env <- rlang::env(
    rlang::caller_env(),
    .foo = rlang::sym(get_foo_col(.data)) # This is wrong, but what to do instead?
  )
  rlang::eval_tidy(expr, env = env)
}

This fails with:

data |> my_mutate(baz = .foo)
#> Error in `dplyr::mutate()`:
#> ℹ In argument: `baz = .foo`.
#> Caused by error:
#> ! object '.foo' not found

Created on 2024-07-18 with reprex v2.1.1

Some things I have tried that didn't work.
  • I can create a data mask from scratch (with rlang::as_data_mask and friends), but I can only figure out how to use this new mask by reimplementing tons of dplyr internals. That is, I don't know how to just "pass" it to the existing dplyr::mutate implementation.
  • I could maybe modify the existing data mask in .data with dplyr:::peek_mask, but that is not publicly exposed, so I'd rather avoid that.
  • I tried delaying execution with rlang::env_bind_active, but cannot find a solution that works in general. In particular, the combination with dplyr::pick seemed attractive, but I cannot quite make it work.
  • Ultimately, I considered just walking the AST in dots to replace .foo leaf symbols with .data[[get_foo_col(x)]], but that feels hacky?

Any pointers on how to approach something like this?

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.