[1] 0.9785184
Maybe few people who use R have forgotten already that R is functional by heart. R has Python dogma OO system, thanks to Reference Class (RC) and R6. R functions can be treated as Lisp’s macros, where it can let you meddle the function and abstract syntax tree (AST) of the function call.
R has few ways to compose a function, divided by three (3) levels:
1 Level 1: Manual Composition
2 Level 2: Using Higher-order Functions
How about we comprises the existing functions and give birth to another function? We can easily make that with purrr::compose(). How about we create a function out of the function? That’s function operators in action.
Higher-order functions create new functions by combining or modifying existing ones, reducing manual code writing.
2.1 Function Composition with purrr::compose()
The nice thing about this is that it takes multiple functions and creates a single new function that applies them in sequence (the default direction is backwards).
So, instead of writing this manually:
We can compose it through purrr::compose():
purrr::compose(sqrt, mean, log)(1:5)[1] 0.9785184
Furthermore, this is also (almost) as readable as using pipe:
I have a different blog mentioning on how bad can the nested function call go.
This function can be read from left to right (by default, it is read vice-versa):
purrr::compose(sqrt, mean, log, .dir = "forward")(1:5)[1] 0.5166883
Which is an equivalent of:
2.2 Partial Application with purrr::partial()
2.3 Function Operators in general
3 Level 3: Programmatic Approach
Swear, this is not easy to do — you need at least better understanding on how to build / generate expressions in R, and this involves understanding metaprogramming in R — Yes, most of the part on manipulating ASTs.
The rlang::new_function() provides an API that programmatically constructs a function expression (yes, it does creates formals, body, and an environment — 3 main components of R functions). Keep in mind that the construction of the function expression with rlang::new_function() happens in runtime.
3.1 Start with the basic first
The new_function() function takes three arguments: formal arguments, the function body, and optionally an environment. Let’s start with a function that squares the number:
[1] 25
Or instead of list() in args parameter, how about using pairlist2() instead?
Alright, you might be asking: What’s the point of using new_function() if we can just use function() instead? The purpose of this
And the thing is, you can automatically generate a function expression with this function.