# magrittr: the R pipe, its placeholder and where it stops helping

> magrittr is the tidyverse package that gives R its pipe operator, rewriting nested calls into left to right sequences. It also introduces a placeholder for argument position and a second operator for exposing variables, both of which come with rules that surprise people.

**tidyverse/magrittr** — Improve the readability of R code with the pipe

- Repository: https://github.com/tidyverse/magrittr
- Website: https://magrittr.tidyverse.org
- Stars: 972 · Forks: 161
- Language: R
- License: NOASSERTION
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/tidyverse-magrittr

## The pipe rewrites a call into a left to right sequence

magrittr offers a set of operators that make code more readable by structuring sequences of data operations left to right rather than from the inside out, by avoiding nested function calls, by minimising the need for local variables and function definitions, and by making it easy to add steps anywhere in a sequence.

The mechanics are simple. The operators pipe their left-hand side values forward into expressions on the right-hand side, so `f(x)` becomes `x %>% f()`, and `x %>% f %>% g %>% h` is equivalent to `h(g(f(x)))`.

The readme's pseudo example shows what that buys you.

```r
the_data <-
  read.csv('/path/to/data/file.csv') %>%
  subset(variable_a > x) %>%
  transform(variable_c = variable_a/variable_b) %>%
  head(100)
```

Four operations, written in the order they execute, with no temporary variables holding intermediate results. If another step is needed later, it is added to the sequence at the point where it belongs.

There is a caveat the readme raises immediately, and it is worth internalising before you rely on the equivalence. The word equivalent is not technically exact. Evaluation is non-standard, and the left-hand side is evaluated before being passed on to the right-hand side expression. In most cases this has no practical implication, but in code that depends on lazy evaluation the pipe changes when things are computed.

The package is written in R, published on CRAN under the name magrittr, and the documentation site is at magrittr.tidyverse.org. The readme points newcomers to the pipes chapter in R for Data Science as the place to start.

## The placeholder moves the value to a chosen argument

By default the piped value becomes the first argument of the function on the right. The argument placeholder `.` lets you put it somewhere else. `x %>% f(y, .)` is equivalent to `f(y, x)`, and `x %>% f(y, z = .)` is equivalent to `f(y, z = x)`.

Using the placeholder several times in the same right-hand side is straightforward. The subtlety is what happens when the placeholder only appears inside a nested expression. In that case magrittr still applies the first-argument rule, because in most cases that produces cleaner code. So `x %>% f(y = nrow(.), z = ncol(.))` is equivalent to `f(x, y = nrow(x), z = ncol(x))`, with the piped value placed first.

That behaviour can be overruled by enclosing the right-hand side in braces. `x %>% {f(y = nrow(.), z = ncol(.))}` is equivalent to `f(y = nrow(x), z = ncol(x))`, and here the placeholder appears in both named arguments.

The practical consequence is that braces change semantics, not just style. A pipeline edited by someone who does not know the rule can silently change which argument receives the data. In review, a brace around the right-hand side is worth a second look, since it is the one place where the same-looking pipeline means something different.

## A pipeline that starts with a dot becomes a function

Any pipeline starting with `.` returns a function which can later be applied to values. Building functions in magrittr is therefore similar to building other values, and the readme shows the equivalence.

```r
f <- . %>% cos %>% sin 
# is equivalent to 
f <- function(.) sin(cos(.)) 
```

This is the part of the package that earns its place in an analysis pipeline rather than just in a script. A transformation that applies to several different data frames can be defined once as a value and then applied, and because it is a function, it can be passed around, stored in a list, or mapped over a set of inputs.

The mechanism is the same rewriting as before, applied in the other direction. The placeholder becomes the function's parameter, and the composition becomes the body. Nothing here needs the function to take a data frame, which means these little helpers work on vectors, matrices or data frames without being tied to a single shape.

Two things are worth noting for maintenance. First, such a function is not self documenting about its argument, since the parameter is literally named `.`. Second, error messages and stack traces from inside the pipeline read as nested calls, which can make a failure in a long chain harder to trace back to the step that caused it.

## %$% exposes the columns when the function has no data argument

Many R functions accept a data argument, `lm` and `aggregate` for instance, which is useful in a pipeline where data is processed first and then passed into such a function. Other functions have no data argument at all, and for those it is useful to expose the variables in the data directly. That is what the `%$%` operator does.

```r
iris %>%
  subset(Sepal.Length > mean(Sepal.Length)) %$%
  cor(Sepal.Length, Sepal.Width)
#> [1] 0.3361992

data.frame(z = rnorm(100)) %$%
  ts.plot(z)
```

The difference in style is visible. With `%>%` the data is passed as the first argument, so the function must accept one. With `%$%` the data is put in scope and the columns can be referred to by name, which is what `cor` and `ts.plot` expect.

The readme is careful that these are separate operators with separate jobs, and that `%$%` is the one to reach for when the callee does not take a data frame. It is also the operator to be careful with, because exposing the environment means the names in the right-hand side resolve against the data rather than against the calling scope, and a column named the same as an object elsewhere in the function will shadow it.

## Installing the package and running your first pipeline

The readme gives three routes to install the package, and you run these in an R session.

```bash
install.packages("tidyverse")
install.packages("magrittr")
pak::pak("tidyverse/magrittr")
```

The first installs the whole tidyverse, which is the easiest way to get magrittr since it comes with the other tidyverse packages. The second installs only magrittr. The third pulls the development version from GitHub, and the readme notes it needs `devtools` enabled, with that install line commented out in the original.

Once installed, attach the package with `library(magrittr)` and the operators become available. A first pipeline looks like the readme example: read a CSV, subset rows, transform a column, and take the first rows, with each step feeding the next.

For anyone new to the package, the readme points to the pipes chapter in R for Data Science as the recommended starting point. The package also publishes a Contributor Code of Conduct, and contributing means agreeing to its terms.

The repository is a standard R package layout with `DESCRIPTION`, `NAMESPACE`, `R/`, `src/`, `man/`, `tests/`, `vignettes/`, `NEWS.md` and a `revdep/` directory for reverse dependency checks. The README.md file is generated from README.Rmd, so documentation changes go through the R Markdown source.

## Where the pipe stops being an improvement

The first limit is that a pipe needs a chain to justify itself. `f(x)` written as `x %>% f()` is not more readable, and for a single call the pipe adds a symbol without removing anything. The gains in the readme's example come from four steps, not from the operator itself.

The second is the evaluation caveat again. Because evaluation is non-standard and the left-hand side is evaluated before the right-hand side expression, a pipeline built from functions with side effects or with default arguments that depend on evaluation order can behave differently from the nested form. The readme says this has no practical implication in most cases. The cases where it does are exactly the ones where you would be surprised.

The third is the placeholder exception. When the placeholder only appears in a nested expression, magrittr applies the first-argument rule anyway. Code that reads as if the placeholder positions an argument somewhere else will do something else, and the fix, braces around the right-hand side, changes the call in a way that is easy to miss in a diff.

The fourth is the project itself. The last push to the default branch was on 2026-04-05 and the newest release is magrittr 2.0.5 from the same day, with 2.0.4 dated 2025-09-13 and 2.0.3 dating back to 2022-03-30. The licence is a custom one rather than a standard OSI identifier, and the repository carries both a `LICENSE` and a `LICENSE.md` file, so read both before redistributing it inside a closed source product.

Finally, the readme does not discuss how the package relates to any pipe built into R itself, so if your organisation has already standardised on a different syntax, that comparison is yours to make.

## Conclusion

magrittr suits R codebases that transform data through several steps and want the reading order to match the execution order, and it is the wrong tool for a script where every function call has one argument and no chain forms, or for a team that has already standardised on a different piping style. Before adopting it, check whether your existing code relies on lazy evaluation, since the readme states that piping is non-standard evaluation with the left-hand side evaluated first, and read the licence files before redistribution, since the project ships a custom licence alongside the standard LICENSE file.

## FAQ

### What does magrittr do?

It provides a set of operators that pipe left-hand side values into expressions on the right, so a sequence of data operations reads left to right in execution order, with fewer nested calls and fewer temporary variables. The main operator is `%>%`.

### How do I install magrittr in R?

Run `install.packages("tidyverse")` to get it with the whole tidyverse, or `install.packages("magrittr")` for just the package. For the development version the readme suggests `pak::pak("tidyverse/magrittr")` with devtools enabled.

### What does the dot placeholder do?

It controls which argument receives the piped value. `x %>% f(y, .)` is equivalent to `f(y, x)`. When the placeholder appears only inside a nested expression, magrittr still applies the first-argument rule, unless the right-hand side is wrapped in braces.

### How do I build a function from a pipeline?

Start the pipeline with `.`. Any pipeline starting with the placeholder returns a function that can later be applied to values, so `f <- . %>% cos %>% sin` is equivalent to `f <- function(.) sin(cos(.))`.

### When should I use `%$%` instead of `%>%`?

Use `%$%` when the function on the right has no data argument, such as `cor` or `ts.plot`, because it exposes the columns of the data into the expression scope instead of passing the data as the first argument.

## Sources

- [Issues](https://github.com/tidyverse/magrittr/issues)
- [Project website](https://magrittr.tidyverse.org)
- [README](https://github.com/tidyverse/magrittr/blob/main/README.md)
- [Releases](https://github.com/tidyverse/magrittr/releases)
- [tidyverse/magrittr on GitHub](https://github.com/tidyverse/magrittr)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/tidyverse-magrittr
