Jesse Leite

September 22nd, 2025

The Glorious Pipe Operator

One of the first game-changing features people learn about in functional programming is the pipe operator, which allows us to pass and transform data fluently through a pipeline.

In Elixir, the |> operator simply takes the result of an expression on the left side, and passes it as the first argument to the next function on the right side:

names |> Enum.sort() |> Enum.uniq()

This is essentially equivalent to passing the output of sort() directly into uniq():

Enum.uniq(Enum.sort(names))

The most obvious benefit of the pipeline is that it restructures nested funtions from an inside-out to a much more readable left-to-right, or even top-to-bottom fashion:

title
|> String.trim()
|> String.downcase()
|> String.replace(" ", "-")

It also eliminates the need for creating temporary variables, and helps with debugging and refactoring, etc.

If you're coming from Laravel, you're probably familiar with Laravel's fluent collection and string helpers:

str($title)
    ->trim()
    ->lower()
    ->replace(" ", "-")
    ->toString();

On the surface, this looks nearly the same as my Elixir example above, but it's actually much more complex under the hood:

  1. Firstly, str() creates an Illuminate\Support\Stringable instance, with the primitive stored on the object.
  2. At this point, we're only allowed to call methods that exist within the Stringable class, like trim(), lower(), and replace().
  3. To remain fluent, each of these methods must return $this under the hood.
  4. At the end of the chain, we're still left with a Stringable instance, so we need to cast the output to string, or call ->toString() to get a primitive string as a result.

If you've ever tried to built your own fluent API in an object-oriented language like PHP, these concepts will be familiar to you:

  • You manage the state on the object yourself.
  • You return $this from every method.
  • You offer a getter method to grab and/or cast that final state at the end of a pipeline.

In Elixir, functional pipelines are much simpler, because the |> operator is implemented at the language level, and it works on any function.

There is no state to manage, the raw output is passed through each step of the pipeline, and no special getter is needed to cast the final result. In fact, you can even pipe functions across modules!

csv
|> String.split(",")
|> Enum.map(&String.trim/1)
|> Jason.encode()

This little functional |> operator is quite the paradigm shift coming from OOP, and I'm finding that it really helps to simplify and improve code readability and composability.

Interestingly, the pipe operator is coming to PHP 8.5! I have little doubt that the PHP and Laravel communities will be all over it!

Thanks for reading!

For updates, follow me on Twitter / X or subscribe via RSS.

Series: Elixir for PHP Devs

In Progress
  1. Introduction
  2. The Glorious Pipe Operator
  3. eyes-emoji

email-icon-emoji feed-icon-emoji linkedin-icon-emoji github-icon-emoji x-icon-emoji

© Jesse Leite