Last active
January 21, 2022 12:55
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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