Elixir has become one of the most loved languages. Elixir
applications are scalable, very reliable, and the syntax is also
quite nice. In this article, we will discuss about how elixir pipe
operator (|>) works
When we use functional programming languages, sometimes code becomes
messy. Let’s take a look at this code, this function returns the
average height of the adults in Delhi state
calculate_avg_height(adults(state('New Delhi')))
We can rewrite this code using the pipe operator
'New Delhi' |> state |> adults |> calculate_avg_height
Pipe operator makes our lives easier and makes our code looks
beautiful. In this section, we build our own pipe operator with <|>
this symbol. Rules of pipe operator are simple, the result of one
function passes as an argument to the next function.
Let’s say we need to perform a series of addition and multiplication
operations. we can simply use a pipe operator
1 |> add(2) |> multiply(5) |> div(15)
In the first stage, 1 will be passed as an argument to add function
In second stage 1 and 2 are added and pass 3 as an argument to
multiply the function
Multiply function performs 3 * 5 and pass 15 to div function and
return 1 as result
Before we jump into building pipe operator. We need to have
knowledge about elixir macros. You can learn about Elixir macros in
this
article
We use macros to build pipe operator. This basic difference between
functions and macros is macros accept representation of code but
functions accepts evaluation of code
We use 1 |> add(2) |> multiply(5) |> div(15) expression as reference
to build pipe operator
Let’s define a pipe macro. It will do pattern matching on the left
and right arguments
defmodule Pipe do
defmacro left <|> right do
end
end
Define the unpipe function to pluck and store all the functions in
array. You noticed that we store all the functions in
AbstractSyntaxTree (AST) format

def unpipe(expr) do
Enum.reverse(unpipe(expr, []))
end
defp unpipe({:<|>, _, [left, right]}, acc) do
unpipe(right, unpipe(left, acc))
end
defp unpipe(other, acc) do
[{other, 0} | acc]
end
We need to define more function to pipe all the functions into a
single function. You can picture the process by observing the
following diagram
In the first stage we will add 1 as an argument to quoted expression
{: add,_, [2]} In next stage we add {:add,_, [1, 2]} as an argument
to {:multiply _, [3]} and so on

We use elixir inbuilt function List.foldl function to perform pipe
operation and return the expression {:div, _, [{:multiply, _,
[{:add, _, [1,2],3]},15]} to the caller
[{h, _} | t] = unpipe({:<|>, [], [left, right]})
value = List.foldl(t,h , fn {x,pos},acc -> pipe(acc,x,pos) end)
def pipe(expr, {call, line, _}, integer) do
{call, line, List.insert_at([], integer, expr)}
end
The resulting module will look like this.
# pipe.ex
defmodule Pipe do
defmacro left <|> right do
[{h, _} | t] = unpipe({:<|>, [], [left, right]})
List.foldl(t,h , fn {x,pos},acc -> pipe(acc,x,pos) end)
end
def unpipe(expr) do
Enum.reverse(unpipe(expr, []))
end
defp unpipe({:<|>, _, [left, right]}, acc) do
unpipe(right, unpipe(left, acc))
end
defp unpipe(other, acc) do
[{other, 0} | acc]
end
def pipe(expr, {call, line, atom}, integer) when is_atom(atom) do
{call, line, List.insert_at([], integer, expr)}
end
def pipe(expr, {op, line, args} = op_args, integer) when is_list(args) do
{op, line, List.insert_at(args, integer, expr)}
end
end
You can test this operator in iex terminal.
iex(2)> require Pipe
iex(3)> import Pipe
iex(5)> 2 <|> div(1) <|> IO.puts
2
:ok
I hope you understand how pipe operator works . If you want to learn
more interesting topics in elixir.