BenNG
BenNG

Reputation: 531

Source code generation in Elixir

I'm currently learning/reading metaprogramming elixir
I managed to generate a function that puts it's name using macros:

defmodule MyMacros do
  defmacro fun_gen(name) do
    atom_name = elem(name, 0)
    str_name = atom_name |> to_string
    quote do
      def unquote(name) do
        IO.puts unquote(str_name)
      end
    end
  end
end

defmodule My do
  require MyMacros
  MyMacros.fun_gen(bar)
end

the result:

iex(1)> My.bar
bar
:ok

so this is great :) but I was wondering if it was possible to generate several functions using a Enum.each or something like that:

defmodule MyMacros do
  defmacro fun_gen(name) do
    atom_name = elem(name, 0)
    str_name = atom_name |> to_string
    quote do
      def unquote(name) do
        IO.puts unquote(str_name)
      end
    end
  end
end

defmodule My do
  require MyMacros
  loop (~w(foo bar baz) do
    MyMacros.fun_gen(item)
  end
end

Is there a way of looping in order to generate source code ? Thank you !

Upvotes: 2

Views: 964

Answers (1)

greggreg
greggreg

Reputation: 12085

You could do it without using macros at all:

 defmodule My do

  @methods ~w|one two three|a

  for method <- @methods do
    def unquote(method)() do
      IO.puts unquote(method)
    end
  end

end

produces:

iex> My.one
one
iex> My.two
two

Or with a macro:

defmodule MyMacros do
  defmacro gen_funs(names) do
    for name <- names do
      quote do
        def unquote(name)() do
          IO.puts unquote(name)
        end
      end
    end
  end
end

defmodule My2 do
  require MyMacros
  MyMacros.gen_funs([:one, :two, :three])
end

produces:

iex> My2.one
one
iex> My2.two
two

Note: We are passing the list directly to gen_funs rather than a sigil or a variable containing the list. We must do this since macros receive their arguments quoted. This is why you must do the looping in the macro rather than the module using the macro.

Upvotes: 4

Related Questions