Define functions in module metaprogrammically

I have three identical methods in my module, that do (almost) exactly the same thing. Instead of repeating the function definitions, I am trying to define them once to keep code minimal and same for all of them.

So far I’ve tried using Code.eval_string:

defmodule MyModule do
  Enum.each ~w(method1 method2 method3), fn method ->
    @method method

    Code.eval_string """
      def #{@method}(map) when is_map(map) do
        do_map_stuff(:#{@method}, map)
      end

      def #{@method}(arg) do
        do_other_stuff(:#{@method}, arg)
      end
    """
  end

  ## Other Methods

end

but this throws ArgumentError:

Compiling 1 file (.ex)
** (ArgumentError) cannot invoke def/2 outside module
    (elixir) lib/kernel.ex:4297: Kernel.assert_module_scope/3
    (elixir) lib/kernel.ex:3299: Kernel.define/4
    (elixir) expanding macro: Kernel.def/2
    nofile:1: (file)

I think quote/unquote might be the way to go, but I’m not exactly sure how to do this using them (I’ve already read the Meta Guide on the Elixir website).

Don’t need to use Code.eval_string; you can wrap a def in a “loop” and use unquote/1 to “extract” the variable into the context that you’re def-ing in:

defmodule M do
  Enum.each ~w(foo bar), fn(method_name) ->
    def unquote(:"#{method_name}")(), do: unquote(method_name)
  end
end

There’s no need to use eval_string, you could use Code.eval_quoted but it’d be much much better to just define a macro in another module and then import it.

Here’s a quick example, you can put on a file foo.ex and execute with elixir.

defmodule Gen do

  # Note that macro arguments and return values are quoted expressions
  defmacro gen({name, _, _}) do
    quote do

      # here we define it dinamically
      def unquote(name)() do
        IO.inspect(unquote(name))
      end

    end
  end
end

defmodule Foo do
  import Gen
  gen foo
end

Foo.foo

Thank you. Someone just posted this as an answer on my SO question as well.

http://stackoverflow.com/a/40250462/1533054