Your f() is my Command

Most data scientists are not software engineers by training, and do not see studying computer science concepts as the best investment of their precious time. That’s fine and completely justifiable, but it does affect their confidence as programmers, especially in relation to their developer colleagues. In the case of R, the situation is not helped by the fact that R is a functional programming (FP) language, whereas software engineers are often focused on object-oriented programming (OOP).

Which is why I find this series of blog posts on Design Patterns in Clojure so useful.

Design Patterns?

Clojure??

All right, two steps back.

Design Patterns refers to a 1994 book by the so-called ‘Gang of Four’ (GoF), describing 23 recipes for object-oriented programming. To this day it is highly influential in parsing and solving common software engineering problems. But, it is specific to OOP.

Clojure is a functional programming language - a Lisp dialect running on the Java Virtual Machine. This recent blogpost extols its virtues and is what got me looking deeper into the language. Now, I am not suggesting that data scientists should become proficient in Clojure. What really matters is that R is also a descendent of Lisp, that R is also a functional programming language, and that Clojure is a mature language which has already deeply explored the application of the functional programming paradigm to modern software engineering.

I think you can see where we’re going now: Can R users understand their language better by comparing it to a similar language? And from paths already trodden by languages like Clojure?

The blog series linked above is not the first one to explore how the building blocks of classic OOP design translate to functional programming (see for instance here, here, here or here), but I find it very accessible and pragmatic, and an excellent way to get to know the idioms of a language in relation to accepted common practices. In the coming months I intend to walk through some of the episodes, dig into the Clojure code, and translate the essence of the solution into R.

First up: Command!

The pattern

The GoF definition of the Command pattern is: ‘Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations’.

That’s a mouthful, but essentially it means: Separate the full definition of the action you want to perform (your ‘request’) from its actual execution. This allows you to explicitly manage all the tasks your application is being asked to do, for instance in the interest of resource allocation.

Episode 1 focuses on the problem of logging in and out of a website. This is not a very R type of problem though, so let’s try something else. We simply want to print a string given some inputs, but also want to introduce a delay with which all requests are executed. The delay has no purpose, except to show that the execution of the request is under our control.

We don’t strictly need any design pattern to do this. For instance:

compose_quote <- function(body_part, creature_name) {
  cat(paste0("Go for the ", body_part, ", ", creature_name, "!\n"))    
}

Sys.sleep(1)
compose_quote("eyes", "Boo")
## Go for the eyes, Boo!

This achieves our goal perfectly. But it would be better to have a uniform, centralised way of exerting our influence over request execution.

Clojure

Before we jump ahead to the solutions, let’s implement the above code in Clojure.

(defn compose-quote [body-part creature-name]
  (println (str "Go for the " body-part ", " creature-name "!")))

(Thread/sleep 1000)
(compose-quote "eyes" "Boo")
## Go for the eyes, Boo!

This probably needs a bit of explanation. Essentially, parentheses indicate a function execution, of the form (function-name arg1 arg2 ...). defn in this case can be understood as a function which allows defining a function, and str is analogous to paste0() in R. Knowing that, you can probably figure out how this code works. Install Clojure and play around with the code if you like.

The first proposed solution to the Command design pattern is then as follows:

(defn execute [command]
  (Thread/sleep 1000)
  (command))

(execute #(compose-quote "eyes" "Boo"))

The trick here is understanding what #() does - it defines an anonymous function, which in this case essentially wraps a function call to compose-quote with the specified arguments. The execute function takes a single argument: The anonymous function object. This does achieve the desired goal of handling execution separately from the definition of the request, but the second solution seems slightly better:

(defn execute [command & args]
  (Thread/sleep 1000)
  (apply command args))

(execute compose-quote "eyes" "Boo")

Now we pass the compose-quote function object and its arguments separately to the execute function. Instead of one argument, we now accept an unknown number of arguments in addition to command. This is known as a variadic function, and implemented using the & symbol in the argument list. Next, apply simply performs the function call using the list of arguments provided (very different from the R apply() function). What’s the advantage of this solution? Well, the execute function now has separate access to the elements of the request. This could come in handy when the handling of request execution is more complicated than simply introducing a delay.

The crucial insight here is that because functions are first-class citizens in functional programming (objects like any other), Clojure already has the capability to pass around and manage requests, as a function object plus its parameters. OOP uses a rather verbose pattern to implement Command, whereas in FP it’s a freebie.

All right, job done then! But how about R?

R

We could of course port the original OOP design pattern to R using S3 or S4 implementations - but that is not the purpose of this blogpost. Today is a summer day, and we embrace R’s functional side!

If you are an experienced R programmer and understood the Clojure code, you already know where this is going, since the Clojure solutions are easily translated into common R idioms. This is the anonymous function solution:

execute <- function(fn) {
  Sys.sleep(1)
  fn()
}

execute(function() compose_quote("eyes", "Boo"))

The second solution can be achieved in a number of ways, each using ... ellipsis of additional function arguments to define a variadic function. By not specifying explicitly which arguments we expect for execute(), the execution handler does not become dependent on a specific type of request, and we also avoid having to pack all arguments explicitly into a single object (for instance a list).

execute <- function(fn, ...) {
  Sys.sleep(1)
  fn(...) 
  # Or: do.call(fn, list(...))
  # Or: rlang::exec(fn, ...)
}

execute(compose_quote, "eyes", "Boo")

If you have ever spent your leisure time browsing the source code of R packages you will have encountered this pattern often, and might not have thought twice about it. But it is important to realise that it lies at the core of what R is as a language, and that although the syntax may be R-specific, the principle is shared with other functional programming languages (such as Clojure).

Conclusion

The Command pattern comes very natural to R, since we can specify both the function to execute and that function’s arguments - as arguments to another function. R is not ‘weird’ in this respect, other functional languages do the same thing. And hopefully you have learned a thing or two about Clojure as well!

Next time, we’ll have a closer look at Episode 2: Strategy.