Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a dynamic function name using Elixir macro?

Tags:

elixir

I want to create a function names dynamically. I wrote this macro

defmacro generate_dynamic(name) do
  quote do 
    def add_(unquote(name)) do
    end
  end
end

And I used it like this:

defmodule AnotherModule do
  generate_dynamic :animal
end

Now, I only get AnotherModule.add_ function defined, whereas I expect AnotherModule.add_animal function.

like image 318
shankardevy Avatar asked May 28 '15 06:05

shankardevy


3 Answers

To achieve this, you can prepend :add_ to the name before unquoting. Also, the parentheses after the method name in this case are required to prevent ambiguities. This should do the trick:

defmacro generate_dynamic(name) do
  quote do 
    def unquote(:"add_#{name}")() do
      # ...
    end
  end
end
like image 137
Patrick Oscity Avatar answered Nov 16 '22 13:11

Patrick Oscity


Sometimes, as a useful shortcut, you can achieve the same result inline, without writing a macro using an unquote fragment.

defmodule Hello do
  [:alice, :bob] |> Enum.each fn name ->
    def unquote(:"hello_#{name}")() do
      IO.inspect("Hello #{unquote(name)}")
    end
  end
end

Hello.hello_bob    # => "Hello bob"
Hello.hello_alice  # => "Hello alice"
like image 41
Paweł Obrok Avatar answered Nov 16 '22 12:11

Paweł Obrok


I did this same sort of thing in a gist to try and mimic Ruby's attr_accessor:

defmodule MacroExp do
  defmacro attr_accessor(atom) do
    getter = String.to_atom("get_#{atom}")
    setter = String.to_atom("set_#{atom}")
    quote do
      def unquote(getter)(data) do
        data |> Map.from_struct |> Map.get(unquote(atom))
      end
      def unquote(setter)(data, value) do
        data |> Map.put(unquote(atom), value)
      end
    end
  end

  defmacro attr_reader(atom) do
    getter = String.to_atom("get_#{atom}")
    quote do
      def unquote(getter)(data) do
        data |> Map.from_struct |> Map.get(unquote(atom))
      end
    end
  end
end


defmodule Calculation do
  import MacroExp
  defstruct first: nil, second: nil, operator: :plus

  attr_accessor :first   # defines set_first/2 and get_first/1
  attr_accessor :second  # defines set_second/2 and get_second/1
  attr_reader :operator  # defines set_operator/2 and get_operator/1

  def result(%Calculation{first: first, second: second, operator: :plus}) do
    first + second
  end
end

https://gist.github.com/rcdilorenzo/77d7a29737de39f0cd84

like image 8
Christian Di Lorenzo Avatar answered Nov 16 '22 13:11

Christian Di Lorenzo