Prerequisites

Decorators use:

To fully understand decorators underlying mechanism, you should be familiar these topics.

How does a decorator look like?

decorator_skeleton <- function(func){
    # Define a new function that perform additional functionality before/after 
    # calling the original function
    wrapper <- function(...){
        # (optional) Do something before
        NULL
        # Call the original function
        result <- func(...)
        # (optional) Do something after
        NULL
        # Return result
        return(result)
    }
    
    return(wrapper)
}

Finally, we apply the decorator to a function (i.e. decorating) with

sum <- decorator_skeleton(base::sum)
class(sum)
#> [1] "function"

Step-by-step example

In this example, we create a basic version for decorators::time_it. Recall, the time_it decorator measures and displays the execution time of a function.

Step 1: Return the original function

time_it <- function(func){
    return(func)
}

Step 2: Wrap the original function and return it (unmodified)

time_it <- function(func){
    
    wrapper <- function(...){
        return(func(...))
    }
    
    return(wrapper)
}

Step 3: Wrap the original function, modify it and return it

time_it <- function(func){
    
    wrapper <- function(...){
        start_time <- Sys.time()
        
        result <- func(...)
        
        end_time <- Sys.time()
        
        print(difftime(end_time, start_time))
        
        return(result)
    }
    
    return(wrapper)
}

Finally, we demonstrate time_it by decorating the sum function

## Before: sum() reports only the sum of its input
sum(1:3)
#> [1] 6

## Decorating sum with time_it 
sum <- time_it(base::sum)

## After: sum() reports both the sum of its input and the function's run time 
sum(1:3)
#> Time difference of 7.867813e-06 secs
#> [1] 6