Macro to create anonymous function to execute quoted expression

Hi folks! I have a situation that requires a macro to avoid evaluation of an expression, but I want to transform it into an anonymous function with the expression as its body. For example:

  defmacro runtime_attribute(key, expression) do
    caller = __CALLER__
    func = fn -> unquote(expression) end # big nope here
    current_scope = pop_scope(caller)
    new_scope = put_attribute(current_scope, key, func)
    push_scope(caller, new_scope)
  end

After execution, new_scope[key].() should return the actual value of the expression. What I’m building essentially mimics Absinthe’s notation for building a GraphQL schema (hence the scopes). I want devs to be able to capture the expression for evaluation at runtime and have it compile into a function ready to execute. Been banging my head on this one for hours, please expand my understanding of macros!

Cheers,
Mike

unquote is only valid in a quote context.

I think you might be confusing compile-time execution (the code in the macro body outside of a quote block) and runtime execution (the AST returned from the macro, usually from a quote block.

1 Like

Here’s a simple and incomplete example:

The macro module

defmodule MyMacros do
  defmacro anon(_key, expression) do
    quote do
      fn -> unquote(expression) end
    end
  end
end

The consumer module

defmodule Expr do
  require MyMacros

  def my_fun do
    anon_fun = MyMacros.anon(:key, 1 + 1)
    anon_fun.()
  end
end

Example execution

iex> Expr.my_fun()
2
2 Likes

Hey kip,

Thanks for the response! I think I finally wrapped my head around the differences between compile-time and run-time execution. Here I think what I was struggling with was the idea that a macro expanded in the body of another macro (outside a quote block) will still have its return value AST injected into the current execution, similar to your example. Turned out in the end this wasn’t actually what I needed to do to begin with haha. I managed to get the expression AST injected in the proper place to accomplish the runtime execution I wanted, and didn’t even have to wrap it in a function, huzzah. Thanks again for the time.

Cheers,
Mike