user3096803
user3096803

Reputation:

Is there a way to use a dynamic function name in Elixir from string interpolation like in Ruby?

I want to be able to construct a function call from a string in elixir. Is this possible? The equivalent ruby method call would be:

"uppercase".send("u#{:pcase}")

Upvotes: 1

Views: 568

Answers (2)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

Although the answer by @fhdhsni is perfectly correct, I’d add some nitpicking clarification.

The exact equivalent of Kernel#send from in is impossible, because Kernel#send allows to call private methods on the receiver. In , private functions do not ever exist in the compiled code.

If you meant Kernel#public_send, it might be achieved with Kernel.apply/3, as mentioned by @fhdhsni. The only correction is since the atom table is not garbage collected, and one surely wants to call an indeed existing function, it should be done with String.to_existing_atom/1.

apply(
  String,
  String.to_existing_atom("u#{:pcase}"),
  ["uppercase"]
)

Also, one might use macros during the compilation stage to generate respective clauses when the list of functions to call is predictable (when it’s not, the code already smells.)

defmodule Helper do
  Enum.each(~w|upcase|a, fn fname ->
    def unquote(fname)(param),
      do: String.unquote(fname)(param)
    # or
    # defdelegate unquote(fname)(param), to: String 
  end)
end
Helper.upcase("uppercase")
#⇒ "UPPERCASE"

Upvotes: 3

fhdhsni
fhdhsni

Reputation: 1629

In Elixir module and function names are atoms. You can use apply to call them dynamically.

apply(String, String.to_atom("u#{:pcase}"), ["uppercase"]) # "UPPERCASE"

Depending on your use case it might not be a good idea to create atoms dynamically (since the atom table is not garbage collected).

Upvotes: 0

Related Questions