logo
Kurashizu Blogwhere ideas flow
  • Home
  • News
  • Blog
  • About
  • Home
  • News
  • Blog
  • About

© 2026 Kurashizu. All rights reserved.

Admin·New Post·Service Status
Powered by Cloudflare·30.10.26
← Back to news

Plumbers, chains, and famous painters: The history of the pipe operator in R

Original article
adolfoalvarez.cl|34 points|2 comments|by sinnsro|Oct 9, 2026

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: data→foo→bar\text{data} \to \text{foo} \to \text{bar}data→foo→bar.

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

  1. Dec 30, 2013: Bache creates the plumbr package.
  2. 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").

The Treachery of Images 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 magrittr operator.
  • March 19, 2014: The chain function is deprecated in dplyr.
  • April 14, 2014: dplyr officially adopts the magrittr %% 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

PackageOperatorKey FeatureStatus
ptools/vadrchain()Functional chainingLegacy
dplyr (early)%.%Linear data frame verbsDeprecated
magrittr%%The . placeholderIndustry Standard
pipeR%%Formula & intermediate savingAvailable (Inactive)
wrappr%.%Dot arrow / Env storageNiche