Mastering the Elixir Pipe Operator

Learn how Elixir's pipe operator (|>) simplifies code, making functional programming clean and readable.

Mastering the Elixir Pipe Operator

Updated: February 1st, 2025

Elixir

Pipe operator

The pipe operator (|>) is one of the most powerful and elegant features of Elixir



What is the pipe operator?

The pipe operator (|>) takes the result of the expression on the left and passes it as the first argument to the function on the right. This allows you to create somewhat of a pipeline, which, in my opinion, makes your code easier to follow and read.

Example

You'll most commonly find the pipe operator (|>) used in list processing tasks. Let's walk through a very simple example. Given a list of integers, [1, 2, 3, 4, 5], you want to filter out even numbers, multiply the remainig ones by 5, and then returning the sum (can't get any more real world than that 😌).

There a many different valid ways to do this. Let's explore a few.

Option 1: The ancient philosophers

As an apprentice working for the Emperor, you might decide to use the Enum module to do the task like so:

elixir

      @spec manipulator(list(integer())) :: integer()
def manipulator(list) do
  odd_list = Enum.filter(list, fn x -> rem(x, 2) != 0 end)
  new_list = Enum.map(odd_list, fn x -> x * 5 end)
  Enum.sum(new_list)
end

manipulator([1, 2, 3, 4, 5]) # Returns 45

			

This, of course, will run as expected

Option 2: Total anarchy

This is no different from the Option 1, only that it's shorter and harder to read.

elixir

      @spec manipulator(list(integer())) :: integer()
def manipulator(list) do
  Enum.sum(
    Enum.map(
      Enum.filter(list, fn x -> rem(x, 2) != 0 end),
        fn x -> x * 5 end))
end

manipulator([1, 2, 3, 4, 5]) # Returns 45

			

Total anarchy right?

Option 3: The pipe operator

Now, as a seasoned engineer in the modern world (of course you time jumped from the Emperor's era), you decide to use the pipe operator (|>).

elixir

      @spec manipulator(list(integer())) :: integer()
def manipulator(list) do
  list
    |> Enum.filter(fn x -> rem(x, 2) != 0 end)
    |> Enum.map(fn x -> x * 5 end)
    |> Enum.sum()

  # Or you can use the anonymous function shorthand
  list
    |> Enum.filter(&(rem(&1, 2) != 0))
    |> Enum.map(&(&1 * 5))
    |> Enum.sum()
end

manipulator([1, 2, 3, 4, 5]) # Returns 45

			

For more info on anonymous functions, check out the well written documentation.

Look at that. Pure joy isn't it. It clearly shows the "flow of data".

  1. list is passed to Enum.filter
  2. The result of which is passed to Enum.map
  3. Then finally to Enum.sum

Tips and tricks

Here are a few tips and tricks you should keep in mind while using your new superpower.

Keep it simple

Don't overuse the pipe operator (|>), like where a single function call is used:

elixir

      # Instead of:
[1, 2, 3] |> Enum.sum()
# Do:
Enum.sum([1, 2, 3])

			

Consistent formatting

Align the (|>) on the same 'column' to increase readability:

elixir

      # Instead of:
[1, 2, 3] |> Enum.map(&(&1 * 2))
|> Enum.sum()
# Do:
[1, 2, 3]
  |> Enum.map(&(&1 * 2))
  |> Enum.sum()

			

Debugging

Insert IO.inspect/2 in the pipeline to debug intermediate values.

elixir

      list
  |> Enum.filter(&(rem(&1, 2) != 0))
  |> IO.inspect(label: "After filter")
  |> Enum.map(&(&1 * 5))
  |> IO.inspect(label: "After map")
  |> Enum.sum()

			

Conclusion

As you can see, hopefully, the pipe operator (|>) is quite a powerfull tool for writing clean, readable and maintainable code. By chaining functions together, you can see how data flows and is transformed.

Whether you're manupulating lists, strings or maps, the pipe operator (|>) will make your code more elegant.

Happy piping! 🚀.