Skip to content

Instantly share code, notes, and snippets.

@Lakret
Last active June 16, 2019 15:03
Show Gist options
  • Save Lakret/11ee741d2e2f96ff56bef11d77dd6ca3 to your computer and use it in GitHub Desktop.
Save Lakret/11ee741d2e2f96ff56bef11d77dd6ca3 to your computer and use it in GitHub Desktop.
Recursive variable inlining in a macro
defmodule F do
@moduledoc """
Defines a macro that converts recursive 2-tuples to 3-tuples with the third
element being a sum of the first 2. Supports variable interpolation at any level
of nesting.
"""
defmacro f(quoted) do
inner(quoted)
end
def inner({x, y}) when is_number(x) and is_number(y) do
Macro.escape({x, y, x + y})
end
def inner({x, y}) when is_tuple(x) and is_tuple(y) do
ds = {inner(x), inner(y)}
quote do
unquote(ds)
end
end
def inner({x, y}) do
quote bind_quoted: [x: x, y: y] do
{x, y, x + y}
end
end
def inner(q), do: q
end
import F
iex> x = 15
iex> F.f({1, 2})
{1, 2, 3}
iex> F.f({1, x})
{1, 15, 16}
iex> F.f({{1, 2}, {3, 4}})
{{1, 2, 3}, {3, 4, 7}}
iex> F.f({{1, x}, {3, 4}})
{{1, 15, 16}, {3, 4, 7}}
iex> F.f({{1, 2}, {{x, 4}, {5, x}}})
{{1, 2, 3}, {{15, 4, 19}, {5, 15, 20}}}
iex> Macro.expand_once(
quote do
F.f({{1, 2}, {{x, 4}, {5, x}}})
end,
__ENV__
) |> Macro.to_string() |> IO.puts()
{{1, 2, 3}, {(
x = x
y = 4
{x, y, x + y}
), (
x = 5
y = x
{x, y, x + y}
)}}
:ok
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment