You can’t expand them, because unlike sigil they are special forms. However if you still expect to pass raw map then you can solve that in few ways depends on use case …
defmodule UsedModule do
defmacro __using__(opts) do
if get_config!(opts, __CALLER__).with_extra do
quote do
def extra(), do: "with extra"
end
else
quote do
def extra(), do: "no extra"
end
end
end
defp get_config!(opts, env) do
case opts[:config] do
nil ->
raise ":config option not passed"
{:%, _, [{:__aliases__, _, [:PatternMatchingConfig]}, {:%{}, _, config}]} ->
struct(PatternMatchingConfig, config)
{:%, _, [aliases, {:%{}, _, config}]} ->
aliases |> Macro.expand_literal(env) |> struct(config)
{:%{}, _, config} ->
Map.new(config)
_config ->
raise "unexpected config value. Did you passed a raw map?"
end
end
end
defmodule ExpandedConfig do
defstruct with_extra: false
end
defmodule PatternMatchingConfig do
defstruct with_extra: false
end
defmodule Examples do
defmodule ExpandedStruct do
use UsedModule, config: %ExpandedConfig{}
end
defmodule PatternMatchingMap do
use UsedModule, config: %{with_extra: true}
end
defmodule PatternMatchingStruct do
use UsedModule, config: %PatternMatchingConfig{}
end
end
If you do not want to mix your code with handling special forms then all you need to do is write something like:
defmodule Example do
def sample(ast, env) do
if Macro.quoted_literal?(ast) do
Macro.postwalk(ast, fn
{:__aliases__, _, _} = alias -> Macro.expand(alias, env)
{:%{}, [], opts} -> Map.new(opts)
{:%, [], [module, opts]} -> struct(module, opts)
other -> other
end)
else
ast
end
end
end
iex> map_ast = quote do: %{day: 1, month: 1, year: 2022}
{:%{}, [], [day: 1, month: 1, year: 2022]}
iex> Example.sample(map_ast, __ENV__)
%{day: 1, month: 1, year: 2022}
iex> struct_ast = quote do: %Date{day: 1, month: 1, year: 2022}
{:%, [],
[
{:__aliases__, [alias: false], [:Date]},
{:%{}, [], [day: 1, month: 1, year: 2022]}
]}
iex> Example.sample(struct_ast, __ENV__)
~D[2022-01-01]