The Elixir Pipe and Capture Operators

January 16, 2018

The Elixir Pipe and Capture Operators

When I first came across the use of the elixir pipe (“|”) and capture (“&”) operators in Elixir, I experienced confusion at first.

Elixir basics

I think part of the confusion is because the capture operator is used to convert a function into an anonymous function. It is also used as a “value placeholder”.

The pipe operator acts similar to the pipe operator in Unix in that it passes the result of an expression on to another expression.

Case 1: Piping one argument

Here you can see I’m piping in a list into Enum.map/2 and

[1, 2]
|> Enum.map(fn(x) -> x * 2 end)

Now Enum.map/2 has an arity of 2, meaning it takes 2 arguments. So you could write it out as:

Enum.map([1, 2], fn(x) -> x * 2 end)

Case 2: Piping an argument that is not the first argument to another function

In the command function, you’ll notice I want to pipe an argument into add_code but as its second parameter. Below is how I do that.

You can see I use the capture operator to define an anonymous function with an arity of 2 and pass in the 3 as the rightmost argument.

defmodule X do
  def command(code \\ 2) do
    3
    |> (&(add_code(code, &1))).()
    # output here will be 5 if code is set to 2
  end

  def add_code(code, y) do
    code + y
  end
end

Case 3: Piping an argument to a core library like Enum

Below I’m passing a list into Enum.filter/2 and filtering out only the true values (in this case the number 2).

You’ll notice I’m using the “&” operator to define an anonymous function to “filter” the values. If we were to expand that function it would read fn(x) -> x end.

def commands(code) do
    [2, false]
    |> Enum.filter(&(&1))
    |> (&(reversal(code, &1))).()
  end

Case 4: Passing Named Functions as Arguments

You can see in the below function that pairing a named function and arity with the capture operator results in an anonymous function. In this case, IO.puts ends up being that anonymous function.

Enum.each([1,2,3], &IO.puts/1)

Case 5: Capture with Anonymous functions

You’ll notice below I show 2 ways of defining an anonymous function.

volume = fn(x, y, z) -> x * y * z end
volume = &(&1 * &2 * &3)
volume.(1,2,3)

Summary

Hopefully, you learned a little bit about how to use the capture and pipe operators together. If you’re interested in further reading, this post by DockYard has some nice diagrams and this post has a nice little write-up.


Profile picture

Written by Bruce Park who lives and works in the USA building useful things. He is sometimes around on Twitter.