Skip to content

Instantly share code, notes, and snippets.

@Qqwy
Last active January 21, 2022 12:55
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Qqwy/7d5128443ce04bf5551bd55b2cc8d417 to your computer and use it in GitHub Desktop.
Save Qqwy/7d5128443ce04bf5551bd55b2cc8d417 to your computer and use it in GitHub Desktop.
Elixir example of a pipe-operator that uses some simple metaprogramming to allow piping into capture-syntax.
defmodule Capturepipe do
@doc """
A pipe-operator that extends the normal pipe
in one tiny way:
It allows the syntax of having a bare `&1` capture
to exist inside a datastructure as one of the pipe results.
This is useful to insert the pipe's results into a datastructure
such as a tuple.
What this pipe-macro does, is if it encounters a bare `&1` capture,
it wraps the whole operand in `(&(...)).()` which is the
anonymous-function-call syntax that the Kernel pipe accepts,
that (argubably) is much less easy on the eyes.
So `10 |> {:ok, &1}` is turned into `10 |> (&({:ok, &1})).()`
To use this operator in one of your modules, you need to add the following to it:
```
import Capturepipe
import Kernel, except: [|>: 2]
```
## Examples
Still works as normal:
iex> [1,2,3] |> Enum.map(fn x -> x + 1 end)
[2,3,4]
Insert the result of an operation into a tuple
iex> 42 |> {:ok, &1}
{:ok, 42}
It also works multiple times in a row
iex> 20 |> {:ok, &1} |> [&1, 2, 3]
[{:ok, 20}, 2, 3]
"""
defmacro prev |> next do
# Make sure the pipes are expanded left-to-right (top-to-bottom)
# to allow consecutive applications of the capturepipe to work
prev = Macro.expand(prev, __CALLER__)
# Perform change only if we encounter a `&1` that is not wrapped in a `&(...)`
{_, visible?} = Macro.postwalk(next, false, &capture_visible?/2)
if visible? do
quote do
Kernel.|>(unquote(prev), (&(unquote(next))).())
end
else
quote do
Kernel.|>(unquote(prev), unquote(next))
end
end
end
@doc false
def capture_visible?(ast = {:&, _, [1]}, _bool), do: {ast, true}
def capture_visible?(ast = {:&, _, _}, _bool), do: {ast, false}
def capture_visible?(ast, bool), do: {ast, bool}
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment