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:
Have you seen https://t.co/KTChI5NMYb as.function.formula? Very similar concepts.— BrodieG (@BrodieGaslam) March 26, 2018
He’s right. Gabor Grothendieck’s gsubfn
package is centered
gsubfn function, an extended version of
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
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) #> Loading required package: proto 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
lambdacall altogether, but as far as I have been able to find, the only way to do so would be to redefine
rlang::as_closurewhen passed a formula with anything on the left-hand side.
For now, then, extra keystrokes abide.
gsubfn has found a clever way to minimize keystrokes. It defines an
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
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)) #>  "a1" "b2" "c3" fn$map2_chr(letters[1:3], 1:3, l + n ~ paste0(l, n)) #>  "a1" "b2" "c3"
or for the nested
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.