R decorator to change both input and output
decorator, r
Solution
I think you are pretty much there. Here's an example where the first stage of cleaning is to replace negative input values with NAs, and the output cleaning is simple to negate everything:
format_input <- function(x){
x[x<0] <- NA
return(x)
}
format_output <- function(x){
return(-x)
}
wrapper <- function(f){
force(f)
g = function(bad_input){
good_input = format_input(bad_input)
bad_output = f(good_input)
good_output = format_output(bad_output)
return(good_output)
}
g
}
Then:
> wrapper(sqrt)(c(-2,2))
[1] NA -1.414214
`wrapper(sqrt)` returns a "closure", which is a function with enclosed data. The function `f` has the value of the function `sqrt` as part of that enclosure.
The `force` call is needed since `f` doesn't get evaluated when `g` is created, and in some cases without it then `f` won't get found when running the wrapped version due to R's lazy evaluation or "promises" or something. I'm never exactly sure when this happens but adding a `force` call to unevaluated arguments to closure generators is zero-overhead. Its a bit cargo-cult programming but never a problem.
A more flexible solution might be to specify the input and output cleaning functions as functions to the closure generator, with defaults:
wrapper <- function(f, fi=format_input, fo=format_output){
force(f) ; force(fi); force(fo)
g = function(bad_input){
good_input = fi(bad_input)
bad_output = f(good_input)
good_output = fo(bad_output)
return(good_output)
}
g
}
Then I can wrap `sqrt` with different input and output formatters. For example to change that negative function with a positive one:
> make_pos = function(x){abs(x)}
> wrapper(sqrt,fo=make_pos)(c(-2,2))
[1] NA 1.414214
An even more flexible solution is to spot that you are generating chains of functions here. Your output is `format_output(sqrt(format_output(bad_input)))`. This is function composition and there's a function in the `functional` package to do that:
> require(functional)
> w = Compose(format_input, sqrt, format_output)
> w(c(-2,2))
[1] NA -1.414214
This perhaps gets more useful when you have more than three functions in your composition, you could for example have a list of functions and compose them all together using `do.call`....
Once you see patterns in functional programming its addictive. I'll stop now.
Problem
I am trying to refactor this. In Python, I would use a decorator. What's the 'R'tful way to do this? Say, we have this pattern ``` good_input <- format_input( bad_input ) bad_output <- use_this_func( good_input ) good_output <- format_output( bad_output ) ``` And then again, ``` good_input <- format_input( bad_input ) bad_output <- use_this_other_func( good_input ) good_output <- format_output( bad_output ) ``` As you can imagine, this proliferates like wild mushroom. I want something close to this solution ``` use_this_robust_func <- wrapper( use_this_func ) # Or wrapper( use_this_other_func ) good_output <- use_this_robust_func( bad_input ) ``` I'm trying to wrap the call to `use_this_func` and `use_this_other_func` (and related functions) with `format_input` and `format_output`. Using in part this question, so far I have ``` wrapper <- function( func_not_robust ){ func_robust <- function( ... ){ # This is the bit I haven't figured out ... format_input( ) ... # supposed to convert bad input - the function argument - to good bad_output <- func_not_robust( ... ) # supposed to take good input as argument good_output <- format_output( bad_output ) return( good_output ) } return( func_robust ) } ``` Sorry for the pseudo-code. Note I am not sure that this is the way to go in R. I'm not wedded to the sketch of the solution above, which is born from translating Python - and badly at that - to R. How would a R native do this? Thanks in advance.