(not) Fun with unquote fragments

As part of the re-architecting of ex_cldr I need to generate a module that hosts the CLDR data (similar to Phoenix, Gettext, Ecto). In one case I’m unable to insert a generated AST as a function body where the function is defined inside a quote block.

How to i get the function_body in scope so I can unquote/1 it?

defmodule TestMod do
  def generate do
    quote location: :keep do
      for locale_name <- [1,2,3] do
        # function_body is a generated AST
        function_body =
          quote do
            unquote(locale_name)
          end

        # ** (CompileError) undefined function locale_name/0
        # ** (CompileError) undefined function function_name/0
        defp test(unquote(locale_name)) do
          unquote(function_body)
        end
      end
    end
  end
end

Note that this works (same code in a module context):

defmodule TestMod do
  for locale_name <- [1,2,3] do
    # function_body is a generated AST
    function_body =
      quote do
        unquote(locale_name)
      end

    # Compiles and runs fine
    defp test(unquote(locale_name)) do
      unquote(function_body)
    end
  end
end

I assume this is related to nested quote contexts but I can’t figure out the right dereferencing.

I’ll link to a more elaborate answer, but in short unquote has two functionalities, which cannot be used at the same time in a macro.

3 Likes