R Tip: Use Inline Operators For Legibility

R Tip: use inline operators for legibility.

A Python feature I miss when working in R is the convenience of Python‘s inline + operator. In Python, + does the right thing for some built in data types:

  • It concatenates lists: [1,2] + [3] is [1, 2, 3].
  • It concatenates strings: 'a' + 'b' is 'ab'.

And, of course, it adds numbers: 1 + 2 is 3.

The inline notation is very convenient and legible. In this note we will show how to use a related notation R.

To be clear: when working in a language it is important to learn to write code that is idiomatic for that language. Otherwise you are fighting the language, and writing code that may be hard for other users to work with (as it won’t match the learnable expectations of the language). The Python community has formalized this concept as “Pythonic”, which means Python Enhancement Proposal (PEP) 8‘s style recommendations plus a number of community conventions. The R situation is less formal, but “R-like” can include some important concepts such as: writing in a functional style, working vectorized, and a number of other concepts.

My note on Timing the Same Algorithm in R, Python, and C++ was a deliberate example of “writing C/C++ style code” in C++ (where that makes sense) plus R and Python (where that can be awkward). In fact I left the semi-colons in the C-style (scalar oriented) to R transliteration to emphasize how alien to R this code is (and later removed them in the more “R-like” vectorized translation).

However, if a good idea from one language works well in another language, then there is a good argument for implementing an analogue. The is no strong reason to leave one language less convenient than another.

For example: in Python range(a, b) returns an iterator that enumerates the integers from a through b-1 if b > a, and the empty iterator otherwise. This is the exact right iterator in a zero-indexed language (such as Python) for driving for-loops and list-comprehensions. R doesn’t have an operator so closely adapted to its indexing needs (the closest being seq_len() and seq_along()). So R is missing a bit of the convenience of this Python feature. However it is easy to add an R-version of such a feature, and this is found in wrapr::seqi(). Note wrapr::seqi() is not a direct translation of Python’s range(); wrapr::seqi(a, b) generates the range of integers a through b inclusive (if b >= a), as this is the convenient interval notation for a one-indexed language (such as R).

Now back to Python’s + features.

The wrapr package (available from CRAN) supplies some nice related inline operators including:

  • %c%: c(1,2) %c% 3 is 1, 2, 3 (named after R’s c() function).
  • %p%: "a" %p% "b" is "ab" (named after R’s paste0() function).

The above code is assuming you have the wrapr package attached via already having run library('wrapr').

Notice we picked R-related operator names. We stayed away from overloading the + operator, as the arithmetic operators are somewhat special in how they dispatch in R. The goal wasn’t to make R more like Python, but to adapt a good idea from Python to improve R.

The general purpose of wrapr package is to provide extensions to make working in R incrementally more convenient while preserving an “R-like” style. It might not seem worth it to bring in a whole package for one our two such features. However, wrapr is a very lightweight low-dependency package. And wrapr includes many useful extensions- all documented with examples (and many of which are covered in earlier tips).