Plumbers, chains, and famous painters: The history of the pipe operator in R
Plumbers, Chains, and Famous Painters: The Evolution of the R Pipe Operator
By Adolfo Álvarez (Rewritten)
The chronicle of the pipe operator in R is a journey of community collaboration, starting not in a formal lab, but on the forums of StackOverflow.
🛠️ The Genesis (2012)
On January 17, 2012, an anonymous contributor known as user4 posed a pivotal question:
"How can you implement F# s forward pipe operator in R?"
The goal was to enable the chaining of calculations so that instead of nested functions, one could write a linear sequence: .
Early Prototypes
Ben Bolker responded almost immediately with a rudimentary implementation:
`% %` - function (x,f) do.call (f, list (x))
pi % % sin % % cos
## [1] 1
Shortly after, on April 5, 2012, Peter Meilstrup introduced the ptools package (later renamed to vadr), which featured a function called chain to handle argument piping.
📈 The Rise of dplyr and the "Inside-Out" Problem
On October 28, 2012, Hadley Wickham launched the dplyr project as a successor to plyr. While it didn't have a pipe at launch, by October 9, 2013, the package introduced both a chain function and the %.% operator.
The motivation was purely ergonomic. Without a pipe, dplyr code required a "nested" reading style—essentially reading from the inside out.
The "Old" Way:
library(hflights)
library(dplyr)
filter(
summarise(
select(
group_by(hflights, Year, Month, DayofMonth),
Year:DayofMonth, ArrDelay, DepDelay
),
arr = mean(ArrDelay, na.rm = TRUE),
dep = mean(DepDelay, na.rm = TRUE)
),
arr > 30 | dep > 30
)
The "Piped" Way:
Using the %.% operator, the logic became linear:
hflights %.%
group_by(Year, Month, DayofMonth) %.%
select(Year:DayofMonth, ArrDelay, DepDelay) %.%
summarise(arr = mean(ArrDelay, na.rm = TRUE), dep = mean(DepDelay, na.rm = TRUE)) %.%
filter(arr > 30 | dep > 30)
🎨 Enter magrittr and the Surrealist Influence
The %.% operator was short-lived. On December 29, 2013, Stefan Bache proposed a more powerful version of %% that utilized substitute and a placeholder . to allow for more flexible function calls.
The Naming Journey
- Dec 30, 2013: Bache creates the
plumbrpackage. - Jan 1, 2014: The package is renamed to
magrittr.
The name is a clever nod to the Belgian surrealist René Magritte and his famous painting The Treachery of Images (which features a pipe and the caption "Ceci n'est pas une pipe").
Caption: The inspiration behind the name magrittr.
Convergence and Integration
The dplyr and magrittr paths merged quickly:
- Jan 2014: Stefan Bache alerts Hadley Wickham to the
magrittroperator. - March 19, 2014: The
function is deprecated inchaindplyr. - April 14, 2014:
dplyrofficially adopts themagrittr%%operator because it is physically easier to type (requiring only the Shift key).
IDE Support: On July 31, 2014, RStudio added a keyboard shortcut. It started as Shift+Alt+. but eventually became the standard Ctrl+Shift+M.
🗺️ The Pipe Ecosystem Map
🧪 Alternative Implementations
While magrittr became the standard, other developers explored different philosophies:
1. pipeR (April 2014)
Created by Kun Ren, pipeR aimed for maximum flexibility. It allowed users to:
- Use the pipe as the first argument.
- Use
.within an expression. - Use formulas for piping.
- Save intermediate results via
~. - Extract elements by name using
().
Example of pipeR complexity:
library(pipeR)
mtcars % % (lm(mpg ~ wt + cyl, data = .)) % % (~ lm_mtcars) % % summary % % (r.squared)
## [1] 0.8302274
2. wrappr (July 2018)
This package introduced the "dot arrow pipe" %.%, where the left-hand side is preserved in the environment.
# Example from wrappr
mean(.)
## [1] 5.5
📊 Summary of Pipe Operators
| Package | Operator | Key Feature | Status |
|---|---|---|---|
ptools/vadr | chain() | Functional chaining | Legacy |
dplyr (early) | %.% | Linear data frame verbs | Deprecated |
magrittr | %% | The . placeholder | Industry Standard |
pipeR | %% | Formula & intermediate saving | Available (Inactive) |
wrappr | %.% | Dot arrow / Env storage | Niche |