# Anonymous Functions, Part II: gsubfn

gsubfn's as.function and fn$* notation 3 minute read This is a follow-up to Anonymous Functions, Not Variables. For context, read that first. After my previous post, Brodie Gaslam pointed me in an interesting direction on Twitter: He’s right. Gabor Grothendieck’s gsubfn package is centered around its gsubfn function, an extended version of gsub whose replacement parameter can accept functions, e.g. to do arithmetic with numbers in a string. That anonymous function can be written as a formula, a capability enabled by a as.function.formula function which is also exported. It provides very similar functionality to the lambda function I defined, so the nested map example could be written library(purrr) map(c("a", "b", "c"), gsubfn::as.function.formula(x ~ map_chr(1:3, gsubfn::as.function.formula(y ~ paste0(x, y))))) %>% str() #> List of 3 #>$ : chr [1:3] "a1" "a2" "a3"
#>  $: chr [1:3] "b1" "b2" "b3" #>$ : chr [1:3] "c1" "c2" "c3"

As it appears, it is a method for the S3 generic base::as.function, so when gsubfn is loaded, the syntax can be condensed to

library(gsubfn)

map(c("a", "b", "c"),
as.function(x ~ map_chr(1:3,
as.function(y ~ paste0(x, y))))) %>%
str()
#> List of 3
#>  $: chr [1:3] "a1" "a2" "a3" #>$ : chr [1:3] "b1" "b2" "b3"
#>  $: chr [1:3] "c1" "c2" "c3" and the generic will dispatch to it when passed a formula. The best part of gsubfn::as.function.formula, though, is the alternative way it can be called. At the end of the previous post, I concluded Ideally, it would be nice to drop the lambda call altogether, but as far as I have been able to find, the only way to do so would be to redefine purrr:::as_mapper.default to call lambda instead of rlang::as_closure when passed a formula with anything on the left-hand side. For now, then, extra keystrokes abide. But gsubfn has found a clever way to minimize keystrokes. It defines an fn object with its own class. That class has a method for $ that is rather different than its usual subsetting duties. Instead, it takes a call with a formula in it, and returns that call with the formula converted to a function by as.function.formula (with some safeguards to ignore actual formulas). So while it looks a little weird, at first, you can write

fn$sapply(1:3, i ~ paste0(letters[i], i)) #> [1] "a1" "b2" "c3" fn$map2_chr(letters[1:3],
1:3,
l + n ~ paste0(l, n))
#> [1] "a1" "b2" "c3"

or for the nested map example,

fn$map(c("a", "b", "c"), x ~ fn$map_chr(1:3,
y ~ paste0(x, y))) %>%
str()
#> List of 3
#>  $: chr [1:3] "a1" "a2" "a3" #>$ : chr [1:3] "b1" "b2" "b3"
#>  \$ : chr [1:3] "c1" "c2" "c3"

effectively modifying functions on the fly to accept full formula lambda notation in a beautiful bit of code.