1

I am trying to dynamically create and evaluate a function from a string input and am hung up, again, on meta-programming/evaluation (https://adv-r.hadley.nz/metaprogramming.html). I have a feeling this is answered on SO, but I searched and wasn't able to figure out the solution looking through other posts; however, if there is an existing answer, please let me know and flag as duplicate. Thank you so much for your time and help! Below is a reprex of the issue.

library(dplyr)
library(purrr)
library(rlang)
library(palmerpenguins)

# Create data to join with penguins
penguin_colors <-
  tibble(
    species = c("Adelie", "Chinstrap", "Gentoo"),
    color = c("orange", "purple", "green")
)

# Create function to do specified join and print join type
foo <- function(JOINTYPE) {
  
  # DOESN'T RUN
  # JOINTYPE_join(penguins, penguin_colors, by = "species")
  # call2(sym(paste0(JOINTYPE, "_join")), x = penguins, y = penguin_colors, by = "species")
  
  print(JOINTYPE)
}

# Desired behavior of foo when JOINTYPE == "inner"
inner_join(penguins, penguin_colors, by = "species")
#> # A tibble: 344 x 9
#>    species island bill_length_mm bill_depth_mm flipper_length_… body_mass_g
#>    <chr>   <fct>           <dbl>         <dbl>            <int>       <int>
#>  1 Adelie  Torge…           39.1          18.7              181        3750
#>  2 Adelie  Torge…           39.5          17.4              186        3800
#>  3 Adelie  Torge…           40.3          18                195        3250
#>  4 Adelie  Torge…           NA            NA                 NA          NA
#>  5 Adelie  Torge…           36.7          19.3              193        3450
#>  6 Adelie  Torge…           39.3          20.6              190        3650
#>  7 Adelie  Torge…           38.9          17.8              181        3625
#>  8 Adelie  Torge…           39.2          19.6              195        4675
#>  9 Adelie  Torge…           34.1          18.1              193        3475
#> 10 Adelie  Torge…           42            20.2              190        4250
#> # … with 334 more rows, and 3 more variables: sex <fct>, year <int>,
#> #   color <chr>
print("inner")
#> [1] "inner"

# Use function in for loop
for (JOINTYPE in c("inner", "left", "right")) {
  foo(JOINTYPE)
}
#> [1] "inner"
#> [1] "left"
#> [1] "right"

# Use function in vectorised fashion
walk(c("inner", "left", "right"), foo)
#> [1] "inner"
#> [1] "left"
#> [1] "right"

Created on 2020-10-27 by the reprex package (v0.3.0)

maia-sh
  • 537
  • 4
  • 14
  • 1
    Do a sane and safer approach: Just use `foo <- switch(JOINTYPE, inner = inner_join, <...>); foo(penguins, penguin_colors, by = "species")` – Roland Oct 27 '20 at 11:54
  • 1
    In general non-standard evaluation and dynamic function programming becomes quite cumbersome, and I definitely add my vote onto Rolands "Do a sane and safe approach", as it should have no place in an industrial setting. One option however is to just create the function name as a character string and use `parse(text = [function text])` or `get([function text], mode = 'function')` to extract the function to be called later. – Oliver Oct 27 '20 at 12:01
  • Thanks for your comments! A `switch` is definitely more straightforward; however my situation is a bit more complex than the reprex, so `get` and `rlang::exec` work. – maia-sh Oct 27 '20 at 18:34

1 Answers1

1

One option is to use get() to retrieve the appropriate function:

join <- function(JOINTYPE) {
    get( paste0(JOINTYPE, "_join") )
}

join("inner")(penguins, penguin_colors, by="species")

If using rlang, the more appropriate function here is rlang::exec:

join2 <- function(JOINTYPE, ...) {
    rlang::exec( paste0(JOINTYPE, "_join"), ... )
}

join2("inner", penguins, penguin_colors, by="species")
Artem Sokolov
  • 13,196
  • 4
  • 43
  • 74
  • Thank you Artem! `rlang::exec` was just what I was looking for and works with `map(c("inner", "left", "right"), ~ join2(., penguins, penguin_colors, by="species"))` – maia-sh Oct 27 '20 at 18:38