How to pass AST to macro?

## The elixir `match?` source code:
  defmacro match?(pattern, expr) do
    success =
      quote do
        unquote(pattern) -> true
      end

    failure =
      quote generated: true do
        _ -> false
      end

    {:case, [], [expr, [do: success ++ failure]]}
  end
## This is ok, `pattern` is defined in compile time.
match?({:ok, _}, {:ok, 1}) # => true

## But sometimes I need to generate the `pattern` in runtime, maybe by user input or something else.
ast = quote(do: {:ok, _})
## How to call `match?` correctly?
match?(^ast, {:ok, 1})
match?(unquote(ast), {:ok, 2}) # ->  (CompileError) iex:7: unquote called outside quote

Thanks.

The short answer is: You can’t. Patterns are not a higher level construct on the beam, which could be constructed.

1 Like

I have to disagree with @LostKobrakai here. There very much is a way to do this, because Elixir’s compilation- and metaprogramming constructs are still available at runtime. It just requires a little bit more wrapping:

iex> pattern_ast = quote do {:ok, _} end
iex> input = {:ok, 1}
iex> {result, []} = Code.eval_quoted(quote do match?(unquote(pattern_ast), unquote(input)) end)
iex> result
true

Now would you ever want to do something like this based on user input? Probably not. There are security considerations, as well as the question what a match like this based on user input would actually accomplish.

3 Likes