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.
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.