Search code examples
rubyelixirmetaprogrammingstring-interpolation

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}")

Solution

  • 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"