Iterate through module union type inside macro

for example

defmodule Unionex do
  defmacro unionex(types) do
      # code that iterates through union types
  end
end

> import Unionex
> unionex(:x | :y | :z)

What I’m trying to do is converting the union into an array of those atoms in order to pass it to a library. Is this possible?

I think this might be what you’re after…

defmodule Unionex do
  defmacro unionex(types) do
    ast_to_list(types)
  end

  def ast_to_list(atom) when is_atom(atom) do
   [atom]
  end

  def ast_to_list({:|, _, [atom, rest]}) do
    [atom | ast_to_list(rest)]
  end

end

In the console:

Interactive Elixir (1.10.3) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> import Unionex       
Unionex
iex(2)> unionex(:x | :y | :z)
[:x, :y, :z]

Macros take AST and return AST. If the return isn’t valid AST you’ll get a compile error. It so happens that a list of atoms is valid AST which is why returning the list works in this example. In practise if your list isn’t valid AST then you would need to process it into valid AST.

1 Like

Thanks for the reply! That’s exactly what I was looking for. Also, I’ve just found out that elixir expands that to

{:|, [line: 1],
 [:x, {:|, [line: 1], [:y, :z]}]}

That makes a lot of sense now.
Anyway there is still something I don’t understand

if I have that union type inside a @type:

defmodule MyModule do
  @type x() :: :x | :y | :z
end

is there a way to pass the type x() to the macro? I can’t get it to work like this:

unionex(MyModule.x())

I’m missing something :sweat_smile:

Try it the other way around like this:

@valid_alternatives [:x, :y, :z)
union_type = Enum.reduce(@valid_alternatives, &{:|, [], [&1, &2]})
@type foo :: unquote(union_type)

You could of course wrap up the reduction into your macro. I don’t think it’s possible to get at the type spec at compile time.

3 Likes

Oh ok, this will do the trick thank you!

There is some discussion on the core mailing list about implementing some kind of construct to convert a list to a typespec so in the future maybe this will be more straight forward.

1 Like

Thanks for pointing it out, I’ll be looking forward to it. The interface will definitely become clearer