How to create decorators in R

decorators in r


Introduction

One of the coolest features of Python is its nice ability to create decorators. In short, decorators allow us to modify how a function behaves without changing the function’s source code. This can often make code cleaner and easier to modify. For instance, decorators are also really useful if you have a collection of functions where each function has similar repeating code. Fortunately, decorators can now also be created in R!

The first example below is in Python – we’ll get to R in a moment. If you already know about decorators in Python, feel free to skip below to the R section.

Below we have a function that prints today’s date. In our example, we create two functions. The first – print_start_end takes another function as input. It prints “Starting function call…” prior to calling this input function. Secondly, it calls the function. Lastly, it prints “Finished function call…”.

The other function, todays_date, simply prints today’s date.


import datetime

# defining a decorator 
def print_start_end(f): 
    
    def wrapper(**args): 
        print("Starting function call...") 
  
        f() 
  
        print("Finished function call...") 
          
    return wrapper 
     
def todays_date(): 
     print(datetime.datetime.today())

Now, let’s say we want to call our todays_date function, but would like to wrap it inside the print_start_end function. One way to do that is by having a nested function call:


print_start_end(todays_date)

However, we can also add a decorator to the todays_date like below. In Python, a decorator is created by adding an “@” followed by a function name (the “decorating” function).


@print_start_end    
def todays_date(): 
     print(datetime.datetime.today())

The result of running todays_date() now is below:

python decorators

We get the same result using a decorator as we did using a nested function call above. The nice part of this, however, is that we don’t have to have any nested functions or change the code within todays_date. We can also easily comment out the decorator to turn off this functionality if we wanted to.


#@print_start_end   
def todays_date(): 
     print(datetime.datetime.today())

There’s a whole lot more to decorators. To learn more, check out Powerful Python, or Guide to Learning Python Decorators. The second of these resources focuses solely on decorators, while the first one also covers several other Python features.

Decorators in R

Now, let’s walk through how to create decorators in R. We can do that using an awesome package called tinsel.

To get started, we first need to install tinsel, which requires devtools. If you don’t have the devtools package installed, you’ll need to install that first (install.packages(“devtools”)). Then, you can install tinsel by running the command below.


devtools::install_github('nteetor/tinsel')

Once you’re setup, you can start decorating functions. Let’s recreate the example above using R. The first function is straightforward to reproduce – we’re just changing Python syntax to R.


library(tinsel)

print_start_end <- function(f)
{
  wrapper <- function(...)
  {
      print("Starting function call...") 
  
      f() 
  
      print("Finished function call...") 
  
  }
    
  return(wrapper) 
  
}

Now when it comes to the function we’re decorating – todays_date, we add our decorator using “#.” plus the name of our decorator function – in this case, print_start_end. Essentially, the main difference between creating a decorator here in R vs. Python is that we use “#.” rather than an “@” in the line above a function to signify that we want to decorate that function.


#. print_start_end
todays_date <- function()
{
    print(Sys.Date())
  
}


In order for R to recognize that we want to use decorators, we need to source our R script using tinsel’s source_decoratees function. Here we just need to pass the name of our script.


source_decoratees("test_dec.R")

Now when we call todays_date, the decorated version of the function gets executed.

r decorator

As mentioned, one key use of decorators is to handle collections of functions that have similar repeating lines of code. For example, let’s apply the print_start_end decorator above to two other functions.


#. print_start_end
yesterday <- function()
{
    print(Sys.Date() - 1)
  
}

#. print_start_end
tomorrow <- function()
{
  print(Sys.Date() + 1)
  
}

decorate functions in r

Timing functions with an R decorator

Another nice feature of decorators is that if we make changes to the decorator, we can easily apply these to any of the functions being decorated. For example, below we can change our print_start_end function to time the execution of a function.


print_start_end <- function(f)
{
  wrapper <- function(...)
  {
      start <- proc.time() 
  
      f() 
  
      print(proc.time() - start) 
  
  }
    
  return(wrapper) 
  
}

Then, we can call our functions like below with our updated decorator. This allows us to make changes once rather than for each function.


yesterday()

tomorrow()

change decorator in r

That’s all for now! Please click to follow my blog on Twitter and keep up with my latest posts, or check out some additional resources for learning Python and R by clicking here.

For more on tinsel, see here.