cult3

Using the Pipe Operator in Elixir

Apr 25, 2016

Table of contents:

  1. Why is this a problem?
  2. Using the Pipe Operator
  3. Conclusion

In last week’s tutorial we looked at defining functions and modules.

Elixir is a functional programming language and so defining small and concise functions is a big part of the philosophy of the language.

A function should have a single concern, and so testing functions becomes really easy. In functional languages, you will often want to combine functions by passing the result of one function as the argument to the next.

If you are familiar with Unix Pipes, this is basically the exact same thing.

Elixir has a special Pipe operator for doing just this. In today’s tutorial we will be looking at the Pipe Operator in Elixir.

Why is this a problem?

Before we look at the Pipe Operator, first lets try to understand why this is a problem in the first place.

An important characteristic of a functional programming language is small and concise functions. When you pass a piece of data into a function, the function will act on it and return a new piece of data.

For example, if I want to capitalise a string, I would pass it to the capitalize function on the String module, like this:

String.capitalize("philip")

But what if I also need to reverse the string as well? To do this I would need to pass the return value of the capitalize function to the reverse function:

String.reverse(String.capitalize("philip"))

And what if I also need to split the string at the letter “l”? Well I would need to pass the return value from the reverse function into the split function:

String.split(String.reverse(String.capitalize("philip")), "l")

So as you can probably see, this is getting kinda difficult to read.

In order to figure out what’s going on, you need to find the first function, and then work your way back out.

What’s more, when a function such as split has a second argument, you need to figure out what the argument is by looking at the other side of the line.

And this is not even a particularly complicated example, image if we had even more transformations! This line of code would be very difficult to understand at first glance.

This is where the Pipe Operator comes in.

Using the Pipe Operator

The Pipe operator allows you to pass the return value from one function as the first argument to the second function.

The code from above can be re-written like this using the Pipe Operator:

String.capitalize("Philip") |> String.reverse() |> String.split("l")

So as you can see, this is much easier to read because it naturally flows from left to right. This makes it much easier to understand the data transformations that are taking place.

You will also notice that you don’t need to pass the result from the previous function the next function. This makes the call to String.reverse very clean and simple.

And when you require a second argument, such as the call to String.split, you can simple pass the remaining arguments and the result from the previous function will automatically be used as the first argument.

This makes it really easy to see the arguments of each function without having to read both sides of the line of code at the same time.

When working with source files you can also move each stage onto a new line:

String.capitalize("Philip")
|> String.reverse()
|> String.split("l")

This makes reading the transformation even easier as you can just read it as a list of transformations.

However, you can only do this in source files. If you tried to do this in iex it wouldn’t work because the first line is a complete expression.

Conclusion

The Pipe Operator is a really beautiful part of the Elixir language as it means you don’t have to write a mess of nested functions, or save each return value to a local variable just to pass it to the next function.

As an Elixir programmer you will see the Pipe Operator being used a lot. It’s much better to write really small, and easy to understand functions, and then compose them together.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.