How to dynamically define functions

I wish i can do this

defmodule Foo do
  setfunctionlist  [:elixir, :rocks]
end

And setfunctionlist would define function of name elixir and rocks in the module Foo.
I already know how to do it with one argument like https://stackoverflow.com/questions/30498528/how-to-create-a-dynamic-function-name-using-elixir-macro

Thanks

2 Likes

What shall this compile into? What shall those functions return? Where do you specify there body?

I do not see much sense in this construct…

4 Likes

The functions can return just a string. You can implement setfunctionlist in a another module require the module. Or just Call

Bar.setfunctionlist [:elixir, :rocks]

It doesn t matter.

So you would have

defmodule Bar do
defmacro setfunctionlist arg do
blah…
end
end

Which one?

Whats blah?

What do you want to achieve? What do you want to create?

Do you want this:

defmodule Foo
  [:elixir, :rocks]
  |> Enum.each(fn (name) ->
    def unquote(name)(), do: nil
  end)
end

But in a macro form?

1 Like

blah is actually setfunctionlist implementation.
Yes for the last question

So you are searching for that blah?

Then just use it. The snippet is explicit in what it does, any macro that does exactly the same behind the scenes only obfuscates the code and makes it unmaintanable in the long term.

1 Like

I m searching for blahblah but I need a macro. That my only requirement.
I have already seen your code in the link.

@homologist: I could be wrong, but maybe you are looking for something like this:

1 Like

You are wrong. I really need a macro being able to define a liste of function doing what you want but with the name specify in the list

You can totally do it without a macro, so according to rule 1 you should stop exactly here…

If though you insist, please share you you want those functions to be defer and I’ll look into it tomorrow. But I still do not understand why you don’t just use the enum version I’ve already shown here.

2 Likes

You would get

Foo.elixir => Hell !
Foo.rocks => Yeah!

Let start by that.
Honestly I don t know if it s a trolling contest I asked for help, I had ONE condition that it had to be a macro. And you guys are telling me that I need OOP? Or no macro?

I actually don t know what I want to code, I m just hacking, How could I give you a specification? I can for sure change what I want to do but unless you can explain me why creating a macro that define one function is great and why creating a macro that define a list of function is bad, I m still willing learn.

Is this tightly related to the stackoverflow question you linked in the first post? If so, I think you could just tweak a bit of the generate_dynamic macro to accept a list instead of a single name, i.e. like such:

defmacro setfunctionlist(names) do
  names
  |> Enum.each(fn (name) ->
    quote do
      def unquote(name)() do
        # ...
      end
    end)
end

(I have not tested it.)

https://hexdocs.pm/elixir/Kernel.SpecialForms.html#quote/2-binding-and-unquote-fragments

From where should the macro know what to return for which function? If you only want to insert the function names into the macro, they all need to have the same body, and as such they will all return the same thing. Thats why I asked for an example body.

I do not see where you need OOP, but your question is so vague, that one could see nearly everything in it. We still try to figure out what you actually want.

I still deny to even think about a solution without you understanding your problem.

Currently we know, that you want to create functions from a list of atoms. I’ve shown you a solution for that.

Also we all know, that the first rule of macro writing is to not write them at all, the second is to write them responsibly.

Since you do not even know what you actually want, I do not see responsibility. Also as we have shown, according to what we understood about your problem so far, there are ways to solve it without a macro, so you shouldn’t use one.

Please do yourself a favor and try to understand your problem first. Also take another read of the macro tutorial then and if you then still think that macros are the solution to your problem, feel free to ask again with a proper description of your problem.

Or show us how far you have got so far, some code. Perhaps we can then help you understanding your problem and find a solution together?

Thanks I actually try bind quoted, but after so any other attempts… I guess I didn t ame out the right way

But how?

Please show us some code, that you have tried but that didn’t yield the result you expected. Please also tell us what you had expected from that code. Please help us to help you.

The main trick is binded quoted
defmacro defkv(kv) do
quote do
Enum.each unquote(kv), fn {k, v} →
def unquote(k)(), do: unquote(v)
end
end
end

Won t compile because their an ambiguity. unquote k reference a variable outside the anonymous function.

But this works

defmacro defkv(kv) do
quote bind_quoted: [kv: kv] do
Enum.each kv, fn {k, v} →
def unquote(k)(), do: unquote(v)
end
end
end

So, you have a solution now?

Yes i have

1 Like

Hi,

This is an old thread but couldn’t any recent discussion in this direction so posting my question as it is most relevant here.

I was wondering if @homologist 's solution can be taken yet a step further.

I learned here that it is possible to do so with and without macro:

defmodule My do
  @methods ~w|one two three|a

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

end

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

Now, one step further, what about variable number of parameters?

I came as far as

defmodule MyMacros do

  defmacro gen_fun(name, args) do
    {
      :def,
      [context: Elixir, import: Kernel],
      [
        {
          name, [context: Elixir],
          for(arg <- args, do: {arg, [], Elixir})
        },
        [do: :ok]
      ]
    }
  end

end

defmodule My2 do

  require MyMacros
 
  MyMacros.gen_fun(:abc, [:a, :b])

end

This works but I can’t call MyMacros.gen_fun in a loop iterating over a list function spec e.g.

for {api, args} <- [api1: [:a, :b], api1 : [:a], ...] do: MyMacros.gen_fun(api, args)

I am vaguely getting that this could be a quote / unquote thing (or I am completely wrong).

Is it at all possible, what I am trying to achieve?

This is a pure academic question. I also understand that a solution (if exists) may not be a good practice and / or recommended, just curious.

Please go easy on me quite newbie with elixir :slight_smile:

TIA for satisfying my curiosity!