Phillipp
Phillipp

Reputation: 1445

Elixir Metaprogramming: Inject a list of function calls into a function body

I have a list of tuples with the format {:task, function_name, description} and I want to generate a function that calls the functions defined in the tuples to override a value.

My generated function should look like:

def run(val) do
  val = do_something(val)
  val = do_something_else(val)
  ...

  val
end

Here is my current code:

  defmacro __before_compile__(env) do

    steps =  Module.get_attribute(env.module, :steps) |> Enum.reverse

    iteration = Enum.map steps, fn {:task, func, _} ->
      quote do
        val = unquote(func)(val)
      end
    end

    ast = quote do
      def run(val) do

        unquote(iteration)

        val
      end
    end

    ast
  end

But that generates the following function:

def(run(val)) do
  [val = it_adds_five(val), val = it_adds_ten(val)]
  val
end

As you can see, my function calls are inside a list and only the last function call is applied. When I pass the value 2 into the run function, i get 12 back.

Upvotes: 1

Views: 383

Answers (1)

Dogbert
Dogbert

Reputation: 222118

You can use Kernel.SpecialForms.unquote_splicing/1 instead of Kernel.SpecialForms.unquote/1 to "splice" in a list of AST nodes.

Changing:

def run(val) do
  unquote(iteration)
  val
end

to

def run(val) do
  unquote_splicing(iteration)
  val
end

Generates this AST:

def(run(val)) do
  val = bar(val)
  val = foo(val)
  val
end

for

@steps [
  {:task, :foo, ""},
  {:task, :bar, ""},
]

Upvotes: 2

Related Questions