ggcharts icon indicating copy to clipboard operation
ggcharts copied to clipboard

Standard evaluation functions

Open thomas-neitmann opened this issue 4 years ago • 11 comments

All functions currently use non-standard evaluation which is great for interactive use but IMO clumsy to program with. Having functions that accepts strings as arguments could come in very handy in {shiny} apps.

thomas-neitmann avatar May 21 '20 16:05 thomas-neitmann

Hi, Thomas,

Are you talking about making the kind of syntax below works?

line_chart(data = revenue_wide, x = "year", y = c("Roche", "Pfizer", "Novartis", "Bayer"))

If that is the case I can help.

sjlva avatar Oct 11 '20 20:10 sjlva

Yes, that's exactly what I had in mind. {dplyr} used to have functions that accept strings rather than symbols, i.e. mutate_() rather than mutate(). This is much easier to program with if you know that the inputs are strings.

thomas-neitmann avatar Oct 11 '20 21:10 thomas-neitmann

Nice!

So, what do you think it is a better approach? I read this about NSE and SE, and find out that Hadley Wickham suggests creating one function to SE and another to NSE like you said, so it would be functions like bar_chart() for NSE and bar_chart_() for SE.

But, it is also possible to make an auto evaluation function, like:

auto_evaluate <- function(x) {
  if(is.character(substitute(x))) {
    x <- dplyr::sym(x)
  } else {
    x <- rlang::enquo(x)
  }
  
  return(x)
}

Which would replace in {ggcharts} functions:

x <- rlang::enquo(x)
y <- rlang::enquo(y)
facet <- rlang::enquo(facet)

for

x <- auto_evaluate(x)
y <- auto_evaluate(y)
facet <- auto_evaluate(facet)

Which one do you prefer? Do you know another approaches?

I really would like to contribute, but sometimes I'm not very confident about my knowledge in R and good practices.

Thanks!

sjlva avatar Oct 11 '20 23:10 sjlva

I thought about something like the auto_evaluate function you proposed as well. However, I don't think it works. Imagine a datasets containing a variable called var and foo. What happens in the following scenario?

var <- "foo"
auto_evaluate(var)

var contains a string but is a symbol. I think this kind of ambiguity is the reason Hadley Wickham proposed to have to different functions: one with NSE and one with SE. Another approach is used quite frequently in base R: having a argument character.only that controls how the argument is evaluated. One good example is library:

pkg <- "dplyr"
library(pkg)
Error in library(pkg) : there is no package called ‘pkg’
library(pkg, character.only = TRUE)
Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union

thomas-neitmann avatar Oct 12 '20 17:10 thomas-neitmann

@nathaneastwood How would you handle this?

thomas-neitmann avatar Oct 12 '20 17:10 thomas-neitmann

So I actually use a function in {poorman} for this type of thing: https://github.com/nathaneastwood/poorman/blob/master/R/utils.R#L31 It works ok, but I wouldn't rely on it.

nathaneastwood avatar Oct 12 '20 17:10 nathaneastwood

By the way, is there no rlang solution to this? Since ggplot2 already imports rlang, I would be tempted to find a solution there, the dependency is already there.

nathaneastwood avatar Oct 12 '20 18:10 nathaneastwood

By the way, have you considered the wrapr::let() function? It's fantastic.

nathaneastwood avatar Oct 16 '20 08:10 nathaneastwood

By the way, is there no rlang solution to this? Since ggplot2 already imports rlang, I would be tempted to find a solution there, the dependency is already there.

Yes, there are some possible solutions in rlang. I was considering create some function to convert SE to NSE input but it sounds pretty unsafe.

What do you think about functions like page() and ls()? Both try to automatically determine whether you want standard or non-standard evaluation.

I thought about it a lot and I am very convinced that it would be better to have a function for NSE and another for SE.

sjlva avatar Oct 17 '20 00:10 sjlva

Another approach is used quite frequently in base R: having a argument character.only that controls how the argument is evaluated. One good example is library:

I don’t think it’s a good idea to use an argument to change the behaviour of another argument because it makes function calls harder to understand. Don't you?

What do you think about having a function to SE and another to NSE? Make something like bar_plot() and bar_plot_() ? I think it's the safer approach.

sjlva avatar Oct 17 '20 00:10 sjlva

I think having two separate functions, one with standard evaluation semantics and one with non-standard one is the way to go as it's completely transparent what is going on 👍

thomas-neitmann avatar Oct 18 '20 14:10 thomas-neitmann