Current section
Files
Jump to
Current section
Files
lib/back_pipe.ex
defmodule BackPipe do
@moduledoc """
A simple backwards pipe operator for Elixir via the `~>` operator.
Via the `~>/2` macro, overloads the operator as the backwards pipe. This lets
you pipe the output of preceding logic into the final argument position of
the next function.
### Examples
# Import the operator into your module
import BackPipe
# Calling `use` will work too if you want
use BackPipe
# Basic usage
iex> "hello world" ~> String.split()
["hello", "world"]
# We can chain too
"hello world"
~> String.split()
~> Enum.concat(["oh"])
|> Enum.join(" ")
# "oh hello world"
# More useful: dynamically creating a struct
defmodule Traveler do
defstruct [:id, :name, :location]
def new(kwl) do
kwl
|> Map.new()
|> Map.put(:location, "Unknown")
~> struct(__MODULE__)
end
end
iex> Traveler.new(id: 1, name: "Hal")
%Traveler{id: 1, location: "Unknown", name: "Hal"}
### Why?
Why not!
But really, this is mostly an experiment. Elixir provides both a set of reserved
operators that can be overloaded and a macro system to do so. Plus, the backwards
pipe operator is not a novel concept. Some other functional languages like F#,
have them, and this is a study of what it could look like in Elixir.
Also, it does feel useful for the small handful of cases where the final
argument in a function is often the result of a chain (i.e. pipeline) of operations,
like in the struct example above.
"""
defmacro __using__(_) do
quote do
import BackPipe
end
end
defmacro lhs ~> rhs do
case rhs do
{_, _, args} ->
Macro.pipe(lhs, rhs, length(args))
_ ->
raise ArgumentError,
message:
"Cannot backwards pipe #{Macro.to_string(lhs)} into #{Macro.to_string(rhs)}. " <>
"Can only pipe into local calls foo(), remote calls Foo.bar() or " <>
"anonymous functions calls foo.()"
end
end
end