Why is the variable not available inside the macro?

I’m trying to unquote multiple blocks of code dynamically depending on the include option passed when use is called. When I try to map over the handlers, and pass an anonymous function, I’m getting the error that fun doesn’t exist when unquote(fun)() is called. Not sure what I’m doing wrong as this is the first time I’m making use of this kind of macro voodoo.

defmodule AppWeb.EventHandlers do
  @handlers [timers: :handle_global_update]

  defmacro __using__([include: :all] = _opts) do
    included_handlers = @handlers

    quote do
      Enum.map(unquote(included_handlers), fn {_key, fun} ->
        unquote(fun)()
      end)
    end
  end

  defp handle_global_update() do
    quote do
    ....
    end
  end
end

I think you’re confusing compile-time and runtime code. For example:

quote do
  Enum.map(unquote(included_handlers), fn {_key, fun} ->
    unquote(fun)()
  end)
end

The quote block is runtime code, so fun isn’t known at compile time, only runtime. And therefore shouldn’t be unquoted.

I think you probably meant to have the enumeration occur at compile time. Something like:

for {_key, fun} <- included_handlers do
  quote do
    unquote(fun)()
  end
end

Thank you! Now that I’ve unquoted the fun value like that, I’m getting undefined function handle_global_update/0 for all Live Views which have it included. Seems like this is happening because it’s trying to compile the call to handle_global_update everywhere it’s used instead of taking the quoted expression before compilation.

handle_global_updates is defined in your EventHandlers module, not the modules using it. You need to either import the function or define the call fully qualified as EventHandlers.handle_global_updates().

1 Like