Skip to content

Instantly share code, notes, and snippets.

@lalbuquerque
Created July 13, 2016 02:07
Show Gist options
  • Save lalbuquerque/2d4555561b5aa3f5d57649abf0ba01f5 to your computer and use it in GitHub Desktop.
Save lalbuquerque/2d4555561b5aa3f5d57649abf0ba01f5 to your computer and use it in GitHub Desktop.
@doc """
Pipe operator.
This operator introduces the expression on the left-hand side as
the first argument to the function call on the right-hand side.
## Examples
iex> [1, [2], 3] |> List.flatten()
[1, 2, 3]
The example above is the same as calling `List.flatten([1, [2], 3])`.
The `|>` operator is mostly useful when there is a desire to execute a series
of operations resembling a pipeline:
iex> [1, [2], 3] |> List.flatten |> Enum.map(fn x -> x * 2 end)
[2, 4, 6]
In the example above, the list `[1, [2], 3]` is passed as the first argument
to the `List.flatten/1` function, then the flattened list is passed as the
first argument to the `Enum.map/2` function which doubles each element of the
list.
In other words, the expression above simply translates to:
Enum.map(List.flatten([1, [2], 3]), fn x -> x * 2 end)
## Pitfalls
There are two common pitfalls when using the pipe operator.
The first one is related to operator precedence. For example,
the following expression:
String.graphemes "Hello" |> Enum.reverse
Translates to:
String.graphemes("Hello" |> Enum.reverse)
which results in an error as the `Enumerable` protocol is not defined
for binaries. Adding explicit parentheses resolves the ambiguity:
String.graphemes("Hello") |> Enum.reverse
Or, even better:
"Hello" |> String.graphemes |> Enum.reverse
The second pitfall is that the `|>` operator works on calls.
For example, when you write:
"Hello" |> some_function()
Elixir sees the right-hand side is a function call and pipes
to it. This means that, if you want to pipe to an anonymous
or captured function, it must also be explicitly called.
Given the anonymous function:
fun = fn x -> IO.puts(x) end
fun.("Hello")
This won't work as it will rather try to invoke the local
function `fun`:
"Hello" |> fun()
This works:
"Hello" |> fun.()
As you can see, the `|>` operator retains the same semantics
as when the pipe is not used since both require the `fun.(...)`
notation.
"""
defmacro left |> right do
[{h, _} | t] = Macro.unpipe({:|>, [], [left, right]})
:lists.foldl fn {x, pos}, acc ->
# TODO: raise an error in `Macro.pipe/3` by 1.5
case Macro.pipe_warning(x) do
nil -> :ok
message ->
:elixir_errors.warn(__CALLER__.line, __CALLER__.file, message)
end
Macro.pipe(acc, x, pos)
end, h, t
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment