Tidyeval question: unquoting a symbol

I am working through the tidyeval fundamentals and I have managed to confuse myself on unquoting. This example is contrived - I have set it up to focus on my confusion, it should not be mistaken for a best practice of anything.

library("dplyr")
library("rlang")

birth_year <- 19
filter(starwars, birth_year == birth_year)

As I expect, birth_year is evaluated as a data variable, rather than as a context variable. All rows with non-NA values of birth_year are returned. All is well.

As suggested in the dplyr vignette, I can use quo() as a debugging tool:

quo(
  filter(starwars, birth_year == birth_year)   
)
#> <quosure: global>
#> ~filter(starwars, birth_year == birth_year)

If I want to use the context for the value of birth_year, I unquote it in the call:

quo(
  filter(starwars, birth_year == UQ(birth_year))  
)
#> <quosure: global>
#> ~filter(starwars, birth_year == 19)

All remains well. Let's say that I want to use a string to specify the context variable (contrived, I know). I create a symbol and unquote it:

my_sym <- "birth_year"
quo(
  filter(starwars, birth_year == UQ(sym(my_sym)))
)
#> <quosure: global>
#> ~filter(starwars, birth_year == birth_year)

It almost works, but I can't see how to unquote this so that it looks to the context rather than the data.

Unquoting again does not get me any closer:

quo(
  filter(starwars, birth_year == UQ(UQ(sym(my_sym))))
)
#> <quosure: global>
#> ~filter(starwars, birth_year == birth_year)

Am I missing something here? Thanks!

2 Likes

Someone with better knowledge of tidyeval should probably answer, but I'll give it a shot. I suspect that the issue is that UQ won't actually evaluate a symbol -- it just returns itself. So, you end up having to evaluate it yourself, as below:

library(rlang)

birth_year <- 19
my_sym <- "birth_year"
quo(
  filter(starwars, birth_year == UQ(eval_tidy(sym(my_sym))))
)
#> <quosure: frame>
#> ~filter(starwars, birth_year == 19)
1 Like

@nick Thanks! I could have sworn that I had tried that, but I obviously did not.

I can see that evaluating the symbol solves the problem here, but I remain confused because this usage of UQ() does seem to evaluate the symbol.

quo(
  filter(starwars, birth_year == UQ(birth_year))  
)
#> <quosure: global>
#> ~filter(starwars, birth_year == 19)

My remaining confusion does not diminish my appreciation for your answer, though. :grinning:

You use the usual tool to get the value of a variable from a string representing its name, and since this is independent of tidy eval, you'd do it outside:

library("dplyr")
library("rlang")

birth_year <- 19
my_sym <- "birth_year"
value <- get(my_sym)

filter(starwars, birth_year == UQ(value))

But I think you've made the example a bit too contrived by adding so many layers of indirection

1 Like