The Evolution of R's Pipe Operator: From %>% to |>

How R's pipe operator evolved from the community-built %>% to the native |>, and why it matters.
This article traces the evolution of R's pipe operator: the `magrittr` package introduced `%>%` to replace deeply nested function calls with a readable linear flow, where code order matches processing logic. The package name pays homage to René Magritte's *This Is Not a Pipe*, capturing R's blend of rigor and wit. After `%>%` became the de facto standard in the `tidyverse` ecosystem, R 4.1.0 introduced the native `|>`, bringing the pipe into the language core without any third-party dependency — a testament to how community-driven innovation can shape a language itself.
Why the Pipe Operator Matters
In the world of data analysis, R's pipe operator has become an indispensable tool for many data scientists. It chains a series of data processing steps together in a way that reads almost like natural language — turning code into a coherent narrative rather than a deeply nested mess of function calls that you have to read inside-out.
This piece, inspired by a technical community discussion titled "Plumbers, Chains, and a Famous Painter," traces the history of the pipe operator in R. The title itself carries three metaphors: plumbing represents the flow of data, chains represent the linking of operations, and the "famous painter" points to a well-known cultural reference — Belgian surrealist René Magritte and his painting Ceci n'est pas une pipe (This Is Not a Pipe).
From Nested Calls to Linear Flow
The easiest way to appreciate the pipe operator is to compare it with what came before. Traditional functional-style code often involves deep nesting — something like arrange(summarize(group_by(filter(data, ...), ...), ...), ...). While logically complete, this style forces you to read from the innermost layer outward, placing a heavy cognitive burden on the reader.
The core idea behind the pipe operator is simple: pass the result on the left as the first argument to the function on the right. That tangled nested expression becomes a clean top-down flow — filter first, then group, then summarize, then sort. Each step is clearly visible, and the order you write the code matches the order in which the data is actually processed. This notion of "guiding results downstream like water through a pipe" is exactly where the name comes from.
magrittr and That Famous Pipe
In the R ecosystem, the pipe operator first gained widespread adoption through the magrittr package, with its iconic %>% symbol. The package name itself is a nod to painter Magritte — whose This Is Not a Pipe plays perfectly on the double meaning of "pipe" in both art and programming.
This clever naming wasn't just a throwaway pun. It reflects a characteristic sensibility within the R community: a commitment to serious statistical computing that still leaves room for wordplay, art, and humor. Propelled by the tidyverse ecosystem, the pipe operator quickly became the de facto standard for R users and fundamentally changed how people write data processing code.
The magrittr package was released by Stefan Milton Bache around 2014. The design of %>% draws on the long-standing pipe philosophy from the Unix/Linux world, where | passes the output of one command as the input to the next. The reason %>% uses percent signs is that R reserves this syntax convention for custom infix operators — allowing third-party packages to define functions that look like operators without touching the language parser. magrittr also introduced several companion symbols, including . as a placeholder for piping into non-first argument positions, and %<>% for two-way assignment piping, further extending the expressiveness of the pipe paradigm. As core tidyverse packages like dplyr and tidyr adopted %>% as their preferred syntax, the symbol became the signature style of R data analysis code within just a few years.
The Native Pipe Arrives: |>
As the pipe operator proved its value in practice, R's core development team decided to bring this feature into the language itself, introducing the built-in pipe symbol |>. Unlike %>%, which depends on an external package, the native pipe requires no third-party library — marking the pipe's transition from community experiment to language core.
The two pipes differ in some syntactic details and behaviors, but they share the same design philosophy. The addition of the native pipe serves as official recognition of community innovation, and reflects R's openness to absorbing best practices from its ecosystem. A feature born in the user community eventually fed back into the language itself — a genuinely instructive moment in the history of programming language evolution.
The native pipe |> was officially introduced in R 4.1.0 (released May 2021). Compared to magrittr's %>%, there are a few notable differences: |> is implemented at the language level, incurring no extra function call overhead and offering slightly better performance. However, early versions of |> did not support piping into non-first argument positions — a limitation partially addressed in R 4.2.0 with the introduction of the _ placeholder (used as x |> f(y = _), with syntax that differs from %>%'s .). Additionally, |> does not support magrittr's extended symbols like %T>% (a side-effect pipe useful for debugging). For new projects, the R core team generally recommends the native pipe, while a large body of existing tidyverse-dependent code continues to use %>% — both will coexist for quite some time.
The Design Philosophy Behind a Symbol
The history of the pipe operator looks, on the surface, like the evolution of a symbol. But it really reflects a perennial question in programming language design: how do we make code think more like humans? Transforming deeply nested, machine-friendly structures into linear, human-friendly narratives is precisely what modern languages continually strive for.
From magrittr's %>% to the native |>, R has demonstrated through a single small operator that great tool design doesn't always require grand architecture. Sometimes, it just takes reducing the cognitive cost of reading one line of code. For analysts and researchers who work with data every day, small and continuous improvements like this add up to very real gains in productivity.
A note on sourcing: this article was developed from the title and theme of a technical community discussion. Since the original post contained limited body text, the account of the pipe operator's evolution draws on widely accepted facts within the R ecosystem. Readers are encouraged to consult official documentation for specific technical details.
Related articles

AI SDK Adds 'Interrupted' Finish State: Upgraded Streaming Output Handling
AI SDK's @ai-sdk/xai releases patch 5.0.28, adding an interrupted completion state across OpenAI, Anthropic, Google and 10+ providers to identify silently truncated streaming output, with compatibility changes for finish reason handling.

@ai-sdk/workflow-harness 1.0.160 Patch Release: Dependency Update Analysis
@ai-sdk/workflow-harness releases patch 1.0.160, synchronously updating the @ai-sdk/harness dependency. This article explains the implications of this dependency update, its impact on AI workflow developers, and upgrade recommendations.

AI Colleagues on the Job: A Single-Platform Approach to Real-Time Telecom Fraud Detection
How to embed AI as a fraud-fighting colleague in a single platform for real-time telecom fraud detection: streaming features, model-rule collaboration, human-AI workflows, and challenges.